I'm trying to dynamically creating the ChannelFactory :
var serviceType = GetServiceProxy();
var interfaceType = serviceType.GetServiceInterface(); //return IServiceInterface
var service = new ChannelFactory(binding, address);
the problem is, as you can see, on the second line, where I don't have the generic type, and unfortunately, ChannelFactory does not have an overload that accepts the Type.
Any way around it??
Found that I can only do this with reflection. Of course you also have to call the methods using reflection.
to create the "ChannelFactory" and call the "CreateChannel" method:
private ChannelFactory CreateChannelFactory()
{
var channelFactoryType = typeof (ChannelFactory);
channelFactoryType = channelFactoryType.MakeGenericType(serviceType);
return (ChannelFactory)Activator.CreateInstance(channelFactoryType, binding, address);
}
private object CreateChannel()
{
var createchannel = channelFactory.GetType().GetMethod("CreateChannel", new Type[0]);
return createchannel.Invoke(channelFactory, null);
}
Now the channel is created but since just the interface type is available, I only can get the methods to invoke:
var serviceType = service.GetType();
var remoteMethod = service.GetMethod(invocation.Method.Name);
remoteMethod.Invoke(service, invocation.Arguments);
Hadi: does this forum post here (check out the answer by Roman Kiss, presenting a custom ChannelFactory2 class) address what you're looking for??
If so, you can stop reading my reply :-)
Well, typically you would do this:
1) have your service interface (IMyServiceInterface)
2) create / retrieve your binding and endpoint information
3) Create a channel factory for that interface:
ChannelFactory<IMyServiceInterface> myChannelFactory =
new ChannelFactory<IMyServiceInterface>(myBinding, myEndpoint);
4) from that channel factory, create your client proxy:
IMyServiceInterface client = myChannelFactory.CreateChannel();
5) Call methods on that client:
client.DoStuff();
So which part is it that you want to make more generic / more dynamic, and why?? What's the motivation / driving force behind that idea??
Marc
Related
The issue has already discussed here, but it did not addressed it quite the way I am looking for.
I have already created a service reference from a client console app in visual studio, but I want to do it programmatically with the following contraint:
From Microsoft Docs - wcf, it obvious that we have to have the service interface reference available to the client. In my case I do have the reference available, instead I have the address where the service is hosted and this address is a dynamic one.
So I want to define a customized client class that will have its object declared with the host address only. Lets take the following snippet as an example:
public partial class CalculatorServiceClient : System.ServiceModel.ClientBase<ICalculatorService>, ICalculatorService
{
}
As you can see that ICalculatorService is available while defining the class. What to do if the interface to the service is not available while defining the class.
You can connect to the WCF service pragmatically without having to use the generated class methods, but note that this can have issues if the service changes in future
The idea is simple .
Create a service contract that matches your service implementation
[DataContract]
public class SomeDataContarctClass
{
[DataMember]
public string SomeMember{get;set;}
etc....
}
Create the interface
public IServiceInterface
{
[OperationContract]
List<SomeDataContarctClass> GetSomeData();
...etc
}
Now this is where you start to Glue things together,
Then create the service
public IServiceInterface CreateIService()
{
EndpointAddress myEndpoint = new EndpointAddress("SERVICE URL");
BasicHttpBinding binding= new BasicHttpBinding();
defaultBinding.MaxReceivedMessageSize = 2147483647;
defaultBinding.MaxBufferPoolSize = 2147483647;
defaultBinding.MaxBufferSize = 2147483647;
defaultBinding.ReaderQuotas.MaxArrayLength = 2147483647;
defaultBinding.ReaderQuotas.MaxStringContentLength = 2147483647;
ChannelFactory<IUpdaterService> myChannelFactory = new ChannelFactory<IServiceInterface>(binding, myEndpoint);
myChannelFactory.Endpoint.EndpointBehaviors.Add(new ServiceInterceptionBehavior());
// Create a channel.
return myChannelFactory.CreateChannel();
}
Then you can call the service using
var myserviceImp = CreateIService();
var data = myserviceImp.GetSomeData();
Is there a way to create an async client for a synchronous WCF service without adding a service reference? This is for a .NET 4 client.
A service reference in Visual Studio is nothing else than a code generator that creates a proxy class with corresponding data elements necessary to call your web service. Of course you can hand build a proxy if you really want to go over tedious and boring work.
Maybe start by decompiling System.ServiceModel.ClientBase using .net reflector?
Do some research on ChannelFactory: http://msdn.microsoft.com/en-us/library/system.servicemodel.channelfactory.aspx
Even when implementing my own client by wrapping a ChannelFactory, I am still using the Add Service reference in another project to create the class definitions and move them into the real project. That's a good compromise.
Here's a simple async service interface:
[ServiceContract(Name = "IService")]
public interface IServiceAsync
{
[OperationContract(AsyncPattern = true)]
IAsyncResult BeginGetStuff(string someData, AsyncCallback callback, object state);
IEnumerable<Stuff> EndGetStuff(IAsyncResult result);
}
The .NET contract might look like this:
[ServiceContract]
public interface IService
{
[OperationContract]
IEnumerable<Stuff> GetStuff(string someData);
}
Then in code, assuming you use HTTP, No security and binary message encoding, something like this (Sorry I haven't compiled any of this, just typed it using some of the code I have written for projects):
//Create a binding for the proxy to use
HttpTransportBindingElement httpTransportBindingElement;
httpTransportBindingElement = new HttpTransportBindingElement();
absoluteServiceUri = new Uri(absoluteServiceUri.OriginalString + BinaryEndpointUri, UriKind.Absolute);
}
//Create the message encoding binding element - we'll specify binary encoding
var binaryMessageEncoding = new BinaryMessageEncodingBindingElement();
//Add the binding elements into a Custom Binding
var customBinding = new CustomBinding(binaryMessageEncoding, httpTransportBindingElement);
// Set send timeout
customBinding.SendTimeout = this.SendTimeout;
var factory = new ChannelFactory<IServiceAsync>(customBinding, new EndpointAddress(absoluteServiceUri, new AddressHeader[0]));
var channel = factory.CreateChannel();
channel.BeginGetStuff(Bla, results => { // Do something }, null);
I am doing the following
//Define the service host
this._smeediPluginServiceHost = new ServiceHost(typeof(SmeediServiceHost), smeediServiceUri);
this._smeediPluginServiceHost.AddServiceEndpoint(typeof(ISmeediServiceHost), GetBinding(), smeediServiceUri);
SetupAndStartWebService(_smeediPluginServiceHost);
private void SetupAndStartWebService(ServiceHost serviceHost, ServiceDiscoveryBehavior serviceDiscoveryBehavior = null)
{
//Define service behaviours
ServiceMetadataBehavior serviceMetadataBehavior = new ServiceMetadataBehavior();
serviceMetadataBehavior.HttpGetEnabled = true;
//Add the behaviours to the service
serviceHost.Description.Behaviors.Add(serviceMetadataBehavior);
if (serviceDiscoveryBehavior != null)
serviceHost.Description.Behaviors.Add(serviceDiscoveryBehavior);
serviceHost.Open();
}
I need to pass a parameter to the Service and I can't figure out how. I have looked at How do I pass values to the constructor on my wcf service? but couldn't get my head around it. Thanks
If I understand correctly, you want to pass parameters to the constructor of your service implementation class. You can to this by passing an instance of the service class to the ServiceHost constructor, instead of its type. That is:
// Create the service instance
var instance = new SmeediServiceHost("some parameters");
// Define the service host using the above instance
this._smeediPluginServiceHost = new ServiceHost(instance, smeediServiceUri);
Caution - using this approach means you are using a singleton instance of the service class. If you need a new instance per session or per request, then consider using a ServiceHostFactory as described in this answer.
I have a WCF service that needs to know the Principal of the calling user.
In the constructor of the service I have:
Principal = OperationContext.Current.IncomingMessageHeaders.GetHeader<MyPrincipal>("myPrincipal", "ns");
and in the calling code I have something like:
using (var factory = new ChannelFactory<IMyService>(localBinding, endpoint))
{
var proxy = factory.CreateChannel();
using (var scope = new OperationContextScope((IContextChannel)proxy))
{
var customHeader = MessageHeader.CreateHeader("myPrincipal", "ns", Thread.CurrentPrincipal);
OperationContext.Current.OutgoingMessageHeaders.Add(customHeader);
newList = proxy.CreateList();
}
}
This all works fine.
My question is, how can I avoid having to wrap all proxy method calls in the using (var scope...{ [create header and add to OperationContext]?
Could I create a custom ChannelFactory that will handle adding the myPrincipal header to the operation context? Something like that would save a whole load of copy/paste which I'd rather not do but I'm not sure how to achieve it:)
Thanks
The correct time to set a WCF principal is via IAuthorizationPolicy, by specifying a custom policy in configuration. This covered in full here. If you try setting the principal at other points (an inspector, perhaps) it can get reset by the system.
The problem I am having connecting a wcf client application to a host running on a separate machine is documented in a question previously asked:
WCF: Why does passing in a remote endpoint fail?
However, the solution provided here says you need to use a SpnEndpointIdentity with an empty string. Since my code doesn't look anything like the case in the example I have referenced, I need to know what to do with the SpnEndpointIdentity object I have created.
I have a ChannelFactory upon which I call Create channel, passing in an EndpointAddress:
public override void InitialiseChannel()
{
SpnEndpointIdentity spnEndpointIdentity = new SpnEndpointIdentity("");
var address = new EndpointAddress(EndpointName);
Proxy = ChannelFactory.CreateChannel(address);
}
(NB: ChannelFactory is of type IChannelFactory, where T is the service contract interface)
So what do I do with spnEndpointIdentity? I can't pass it to CreateChannel.
Or perhaps I can use it somehow when I create the channel factory:
private ChannelFactory<T> CreateChannelFactory()
{
var binding = new NetTcpBinding
{
ReaderQuotas = { MaxArrayLength = 2147483647 },
MaxReceivedMessageSize = 2147483647
};
SpnEndpointIdentity spnEndpointIdentity = new SpnEndpointIdentity("");
var channelFactory = new ChannelFactory<T>(binding);
return channelFactory;
}
Again, I can't pass it into the constructor, so what do I do with it?
Thanks.
You almiost got it.
What you're missing is that you associate the EndpointIdentity with the EndpointAddress, and then provide that to CreateChannel():
SpnEndpointIdentity spnEndpointIdentity = new SpnEndpointIdentity("");
var address = new EndpointAddress(EndpointName, spnEndpointIdentity);