Can we use ChannelFactory<T> when the contracts (.NET classes, interfaces) are NOT defined in some common library consumed by client and service? - wcf

I was exploring ChannelFactory and while doing so I did following:
A service contract in assembly named "Common":
namespace Common
{
using System.ServiceModel;
[ServiceContract(Name = "ITestService", Namespace = "http://test/")]
public interface ITestService
{
[OperationContract(Name = "SayHello")]
string SayHello(string request);
}
}
A service hosted under web application called "WcfServiceApp":
Note that I have created another service interface (contract) to create a service. But the names of contracts and the namespaces are same as the contract defined in the "Common" assembly.
namespace WcfServiceApp
{
[ServiceContract(Name = "ITestService", Namespace = "http://test/")]
public interface ITestServiceWithDiffDotNetName
{
[OperationContract(Name = "SayHello")]
string SayHelloAgain(string name);
}
// This service implements new service contract.
public class TestService : ITestServiceWithDiffDotNetName
{
public string SayHelloAgain(string request)
{
return "hello " + request;
}
}
// This service implements common service contract
public class TestService2 : Common.ITestService
{
public string SayHello(string request)
{
return "hello " + request;
}
}
}
There are two ".svc" files (TestService.svc and TestService2.svc), each for services created above. Web.config has following:
<system.serviceModel>
<services>
<service name ="WcfServiceApp.TestService" >
<endpoint binding="basicHttpBinding" contract="WcfServiceApp.ITestServiceWithDiffDotNetName"></endpoint>
</service>
<service name ="WcfServiceApp.TestService2" >
<endpoint binding="basicHttpBinding" contract="Common.ITestService"></endpoint>
</service>
</services>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
A client which calls these two services:
Note that client is using ChannelFactory and the service contract defined in "Common" library.
ChannelFactory<ITestService> commonServiceChannel = new ChannelFactory<ITestService>(new BasicHttpBinding(), "http://localhost/WcfServiceApp/TestService.svc");
var proxy = commonServiceChannel.CreateChannel();
var response = proxy.SayHello("Mike"); // response = "Hello"
ChannelFactory<ITestService> commonServiceChannel2 = new ChannelFactory<ITestService>(new BasicHttpBinding(), "http://localhost/WcfServiceApp/TestService2.svc");
var proxy2 = commonServiceChannel2.CreateChannel();
var response2 = proxy2.SayHello("Mike"); // response2 = "Hello Mike"
Question:
I observed that first service (created using some different service contract) receives the null argument whereas argument received in second service (created using service contract used to create ChannelFactory) is "Mike" as expected.
In Fiddler, I can see that request parameter correctly.
Why does this happen?
If all XML names and namespaces are same (although names of .NET interfaces are different), should the service call not succeed as underlying SOAP messages would be same?
I am afraid what will happen if my customer's applications want to create service in Java and my application is supposed to call it?

Try this, get the WSDL document from each version of your service. Compare the WSDL documents (.NET 4.5 supports single file WSDL documents out of the box) to see what WCF is expecting in the soap message for each service. Chances are a default XML namespace was taken from the .NET (different) namespaces somewhere thus making the "identical" service contracts actually be different. WCF does a lot for you in naming the XML namespaces and you will likely need to manually override those defaults throughout the service, operation and data contracts to make both services support identical soap messages.
On integrating with Java, as long as the Java client can be generated from the WSDL the service outputs there's a chance there won't be any issues. The big exception is configuring the security and authentication aspects of the Java client. This good blog post on specific WCF bindings for Java interop would be worth a look when working with Java based clients.

Thanks to Sixto Saez first.
I compared the WSDL files generated (using svcutil.exe) by both the services and found that they were NOT EXACTLY SAME. However, it was not due to any conflict in the name or namespace of either ServiceContract or OperationContract itself!
It was due to difference in the parameter names used in the definition of the OperationContracts!
You can see that OperationContract "SayHello" has a parameter named "request". On the other hand, OperationContract named "SayHelloAgain" has a parameter named "name". When I changed the name of the parameter from "request" to "name" as it is in second OperationContract, it worked!
So the conclusion is:
ChannelFactory WORKS when the contracts (.NET classes, interfaces) are NOT defined in some common library consumed by client and service. Only thing is that WSDLs generated by those service contracts have to match with each other.

Related

WCF Discovery and DataService V3

I would like to expose discovery endpoints (both TCP and UDP) for my Data Services v3 and enable services to be discoverable from the client and discover them in another application. The main point in the discovery is to get the service endpoint address at the client.
I have tried to adapt the samples that Microsoft have provided for WCF Discovery, but so far I failed to achieve my goal.
I have created a custom Data Service Host Factory on server side:
public class CustomDataServiceHostFactory : System.Data.Services.DataServiceHostFactory
{
protected override System.ServiceModel.ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
var serviceHost = base.CreateServiceHost(serviceType, baseAddresses);
EndpointDiscoveryBehavior endpointDiscoveryBehavior = new EndpointDiscoveryBehavior();
// Create XML metadata to add to the service endpoint
XElement endpointMetadata = new XElement(
"Root",
new XElement("Information", "This endpoint is Data Service v3!"),
new XElement("Time", System.DateTime.Now.ToString("MM/dd/yyyy HH:mm")));
// Add the XML metadata to the endpoint discovery behavior.
endpointDiscoveryBehavior.Extensions.Add(endpointMetadata);
//may be this is not the safest way to set the behaviour
foreach (var endpoint in serviceHost.Description.Endpoints)
{
endpoint.Behaviors.Add(endpointDiscoveryBehavior);
}
// Make the service discoverable over UDP multicast
serviceHost.Description.Behaviors.Add(new ServiceDiscoveryBehavior());
serviceHost.AddServiceEndpoint(new UdpDiscoveryEndpoint());
return serviceHost;
}
}
On the client side I have tried the following code:
DiscoveryClient discoveryClient = new DiscoveryClient(new UdpDiscoveryEndpoint());
// Find service endpoints
// ServiceReference.DataModel is the generated class for the Data Service client proxy
FindCriteria findCriteria = new FindCriteria(typeof(ServiceReference.DataModel));
findCriteria.Duration = TimeSpan.FromSeconds(30);
FindResponse findResponse = discoveryClient.Find(findCriteria);
// Check to see if endpoints were found & print the XML metadata in them.
if (findResponse.Endpoints.Count > 0)
{
foreach (XElement xElement in findResponse.Endpoints[0].Extensions)
{
Console.WriteLine("Printing Metadata from ServiceEndpoint:");
Console.WriteLine("Endpoint Information: " + xElement.Element("Information").Value);
Console.WriteLine("Endpoint Started at Time: " + xElement.Element("Time").Value);
Console.WriteLine();
}
}
Unfortunately this does not work. I get InvalidOperationException:
Attempted to get contract type for DataModel, but that type is
not a ServiceContract, nor does it inherit a ServiceContract.
If I am heading in the right direction I need a way to express the type for the service contract for the discovery. Too bad I am not sure that it will even work like the normal WCF Discovery...
Please share your ideas or even better - working solutions.
I think exception message is clear enough.
For service discovery You try to use type of your data model, while You must use type of your WCF service implementation - this is different things.
Basically DataServicesV3 service adapter uses your data model and exposes it as a WCF service with it's own service contract.
Look at DataServiceV3 type declaration see that it is implementing some interface, i just don't remember name, in this interface declaration you will find [ServiceContract] and [ServiceOperation] attributes. This is Your SERVICE CONTRACT for all ancestors of DataServiceV3. They use THE SAME contract. Here stands another problem I haven't managed to solve yet - how to make WS-Discovery work with DataServices if they share same contract. You'd better dig in this way.

Using Castle Windsor WcfFacility to create client endpoints

I have created three assemblies. A web site, a WCF service and a contracts assembly that holds the interfaces that the services implement. I would like to use Castle Windsor to create the services for me on the client (website) so that I do not have to have an endpoint in the web.config of the web site for each service that I wish to use.
I would like to look at the contract assembly and get all the service interfaces in a namespace. Right now for every service I have something like the following when registering the components with the container.
container.Register(Component.For<ChannelFactory<IMyService>>().DependsOn(new { endpointConfigurationName = "MyServiceEndpoint" }).LifeStyle.Singleton);
container.Register(Component.For<IMyService>().UsingFactoryMethod((kernel, creationContext) => kernel.Resolve<ChannelFactory<IMyService>>().CreateChannel()).LifeStyle.PerWebRequest);
and in my web.config I have the setup code.
<system.serviceModel>
<extensions>
<behaviorExtensions>
<add name="AuthToken" type="MyNamespace.Infrastructure.AuthTokenBehavior, MyNamespace.Contracts" />
</behaviorExtensions>
</extensions>
<behaviors>
<endpointBehaviors>
<behavior>
<AuthToken />
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<binding maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00">
<readerQuotas maxStringContentLength="2147483647" maxArrayLength="2147483647"></readerQuotas>
<security mode="None" />
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint name="MyServiceEndpoint" address="http://someurl/MyService.svc" binding="wsHttpBinding" contract="MyNamespace.Contracts.IMyService"></endpoint>
</client>
</system.serviceModel>
I end up with multiple service endpoints that all look almost exactly the same and when we deploy onto clients machines they have to set the address of every endpoint even though the base url is the same for every one.
I would like to have a base url in my web.config that is grabbed through code and then have the services registered with the container using reflection on the contracts assembly. I do need the specialised endpoint behaviour that is in the above config file.
Where so I start? the WcfFacility looks great but the doco is a bit lacking...
I agree the docs for the wcf facility are lacking and that is sad because it is a really great tool and it would be a real shame if people didn't use it because they could not get started, so let me see if I can help you out a little bit if I can...
Let's create a three project application that has:
A class library for shared contracts
A console application that acts as a server
A console application that acts as a client
The idea is that we want to be able to use the service names when we register the services and to share a base URL (I think that is what you were asking and if not, hopefully you can extrapolate from here). So, firstly, the shared contracts simply has this in it (nothing special, normal WCF fare):
[ServiceContract]
public interface IMyService1
{
[OperationContract]
void DoSomething();
}
[ServiceContract]
public interface IMyService2
{
[OperationContract]
void DoSomethingToo();
}
Now the server console application looks like this, we firstly implement the service contracts (again nothing special there, just classes implementing interfaces) and then just register them all as services (notice no need for any configuration file here and you can change the way you decide what are services etc using all the options that Windsor gives you - my scheme is a bit limited but it gives you an idea):
namespace Services
{
public class MyService1 : IMyService1
{
public void DoSomething()
{
}
}
public class MyService2 : IMyService2
{
public void DoSomethingToo()
{
}
}
}
//... In some other namespace...
class Program
{
// Console application main
static void Main()
{
// Construct the container, add the facility and then register all
// the types in the same namespace as the MyService1 implementation
// as WCF services using the name as the URL (so for example
// MyService1 would be http://localhost/MyServices/MyService1) and
// with the default interface as teh service contract
var container = new WindsorContainer();
container.AddFacility<WcfFacility>(
f => f.CloseTimeout = TimeSpan.Zero);
container
.Register(
AllTypes
.FromThisAssembly()
.InSameNamespaceAs<MyService1>()
.WithServiceDefaultInterfaces()
.Configure(c =>
c.Named(c.Implementation.Name)
.AsWcfService(
new DefaultServiceModel()
.AddEndpoints(WcfEndpoint
.BoundTo(new WSHttpBinding())
.At(string.Format(
"http://localhost/MyServices/{0}",
c.Implementation.Name)
)))));
// Now just wait for a Q before shutting down
while (Console.ReadKey().Key != ConsoleKey.Q)
{
}
}
}
And that is the server, now how to consume these services? Well, actually that is quite easy, here is a client console application (it references just the contracts class library):
class Program
{
static void Main()
{
// Create the container, add the facilty and then use all the
// interfaces in the same namespace as IMyService1 in the assembly
// that contains the aforementioned namesapce as WCF client proxies
IWindsorContainer container = new WindsorContainer();
container.AddFacility<WcfFacility>(
f => f.CloseTimeout = TimeSpan.Zero);
container
.Register(
Types
.FromAssemblyContaining<IMyService1>()
.InSameNamespaceAs<IMyService1>()
.Configure(
c => c.Named(c.Implementation.Name)
.AsWcfClient(new DefaultClientModel
{
Endpoint = WcfEndpoint
.BoundTo(new WSHttpBinding())
.At(string.Format(
"http://localhost/MyServices/{0}",
c.Name.Substring(1)))
})));
// Now we just resolve them from the container and call an operation
// to test it - of course, now they are in the container you can get
// hold of them just like any other Castle registered component
var service1 = container.Resolve<IMyService1>();
service1.DoSomething();
var service2 = container.Resolve<IMyService2>();
service2.DoSomethingToo();
}
}
That's it - hopefully this will get you started (I find that experimenting and using the intellisense usually gets me where I need to go). I showed you both the service and client sides but you can just use one or the other if you prefer.
You should be able to see where the binding is configured and how I have gone about constructing the URLs so in your case you could easily just pluck your base URL from a configuration file or whatever you want to do.
One last thing to mention is that you can add your custom endpoint behaviour by adding it as an extension to the endpoint, so in the client example you would have something like this:
Endpoint = WcfEndpoint
.BoundTo(new WSHttpBinding())
.At(string.Format("http://localhost/MyServices/{0}", c.Name.Substring(1)))
.AddExtensions(new AuthTokenBehavior())

“Could not find default endpoint element that references contract”

While calling webservice during uninstallation of windows application I am getting error as
Could not find endpoint element that references contract
ServiceReference2.IService1' in the ServiceModel client configuration
section. This might be because no configuration file was found for
your application, or because no endpoint element matching this name
could be found in the client element.
I am using Installer class in which I am calling webservice client. Following is code of installer.cs
Source code :
namespace webMiner
{
[RunInstaller(true)]
public partial class demoInstaller : Installer
{
SqlConnection conn = new SqlConnection("Data Source=servername;Initial Catalog=comp;User Id=abc;Password=******;");
public demoInstaller():base()
{
InitializeComponent();
AfterUninstall += new InstallEventHandler(AfterUninstallEventHandler);
}
public override void Uninstall(System.Collections.IDictionary savedState)
{
base.Uninstall(savedState);
Int32 flag = -1;
string keyName = "";
RegistryKey regeditkey = Registry.CurrentUser.OpenSubKey("sevenuser", RegistryKeyPermissionCheck.ReadWriteSubTree);
keyName = regeditkey.GetValue("currentuser").ToString();
webMiner.ServiceReference2.Service1Client sc = new webMiner.ServiceReference2.Service1Client();
flag = sc.unInstallOperation(keyName);
}
}
}
Where unInstallOperation() will call webservice operation which contains updation of account.
How to solve this issue?
Really feedup with this issue
I have no problem when i call serviceclient from another page or from another class file it give me problem when I am calling during uninstallation of application i.e in Installer class. This is app.config client configuration code that i have used
source code:
<client>
<endpoint address="http://companyfind.info/RegWcfService/Service1.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IService1"
contract="IService1" name="BasicHttpBinding_IService1" />
</client>
Is there any need to add this in web.config file of web service??
You probably need to use the name of your endpoint when you are instatiating Service1Client
var sc = new webMiner.ServiceReference2.Service1Client("BasicHttpBinding_IService1");
Or, as it was in my case, you have your another class in another project in solution and two app.config classes. So, you need to copy/paste the desription af enpoints and bindings in main app.config.
Try to update the service reference, and check if client configuration is in the startup project config file.

How do I add WCF client endpoints programmatically?

I need my service to consume other services, and I need to configure these dependencies in code. How do I do this?
This is very simple in config via the following (example):
<client>
<endpoint name="registerService"
address="http://127.0.0.1/registration/" binding="basicHttpBinding"
contract="*"/>
</client>
But for some reason finding the code equivalent is not as easy as I thought it'd be.
If you're using the Visual Studio generated proxy (via "Add Service Reference..."), then you're using the ClientBase abstract class & you'll have a number of constructors that allow you to pass in a config section, an endpoint, a binding etc.
http://msdn.microsoft.com/en-us/library/ms576141.aspx
And if you're instantiating a ChannelFactory then you again have a number of constructors to use.
http://msdn.microsoft.com/en-us/library/ms576132.aspx
// create bindings & endpoints
var binding = new System.ServiceModel.BasicHttpBinding();
var endpoint = new EndpointAddress("http://localhost/MyService.svc");
var factory = new ChannelFactory<IMyService>(binding, endpoint);
var channel = factory.CreateChannel();
// then call your operations...
channel.MyOperation();

access HttpContext.Current from WCF Web Service

I just started using WCF Services with ASP.NET AJAX. I instantiate my WCF service from Javascript and then pass string variables as arguments to my WCF Service method (with an OperationContract signature). I then return a .NET object (defined with a DataContract) which is bound to my custom Javascript class. I'm having trouble authenticating based on the user logged into my web session. However, the WCF web service is a completely different service with no context to the HttpContext.Current object. What is the most secure way to get access to that object?
You can get access to HttpContext.Current by enabling AspNetCompatibility, preferably via configuration:
<configuration>
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
</system.serviceModel>
</configuration>
That in turn allows you to get access to the current user: HttpContext.Current.User - which is what you're after, right?
You can even enforce AspNetCompatibility by decorating your service class with an additional attribute:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
(In the System.ServiceModel.Activation namespace.)
If that attribute is in place, your service will fail to start unless AspNetCompatibility is enabled!
You do not have a HttpContext by default but you have many of the same objects present in the OperationContext (which is always present) or the WebOperationContext (which is only available for certain bindings.
You can access the OperationContext or WebOperationContext by using the static .Current property like so: WebOperationContext.Current
In case you don't want to change Web.config or you cannot change it:
private string GetClientIPAddress()
{
var props = OperationContext.Current.IncomingMessageProperties;
var endpointProperty = props[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
if (endpointProperty != null)
{
if (endpointProperty.Address == "::1" || String.IsNullOrEmpty(endpointProperty.Address))
return "127.0.0.1";
return endpointProperty.Address;
}
return String.Empty;
}