What's the difference between ChannelFactory<T> and ClientBase<T> - wcf

can you explain to me please, What's the difference between ChannelFactory and ClientBase and which of them is better to use for a SelfHost Client-Server Apllication.
using(ChannelFactory<MyInterface> cf = new ChannelFactory<MyInterface>("Endpoint From Web.Config")){}
public class MyClient : ClientBase<MyInterface>, MyInterface {}
Thanks

Let's have an interface that defines the contract:
[ServiceContract]
public interface TheInterface
{
[OperationContract]
string DoWork( string Work );
}
The ChannelFactory automatically creates a proxy that only implements the interface.
var factory = new ChannelFactory<TheInterface>( new BasicHttpBinding() );
var address = new EndpointAddress( "http://..." );
var client = factory.CreateChannel( address );
// there are no other methods on the "client" reference
// than the interface's DoWork method
client.DoWork( "foo" );
On the other hand, a class that inherits from the ClientBase not only implements the interface but also exposes multiple additional properties that can be used to alter the behavior of the client.
public class TheInterfaceProxy : ClientBase<TheInterface>, TheInterface
{
public TheInterfaceProxy( Binding binding, EndpointAddress address ) : base( binding, address ) { }
public string DoWork( string Work )
{
return this.Channel.DoWork( Work );
}
}
And then
var address = new EndpointAddress( "http://..." );
using ( var client = new TheInterfaceProxy( new BasicHttpBinding(), address ) )
{
// DoWork is here
// but multiple other members are there too
// for example - applying a custom endpoint behavior:
client.Endpoint.EndpointBehaviors.Add( new InspectorBehavior() );
client.DoWork( "bar" );
}
where an example simple behavior is used to inspect incoming/outgoing messages at the client side
class InspectorBehavior : IEndpointBehavior
{
#region IEndpointBehavior Members
public void AddBindingParameters( ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters )
{
}
public void ApplyClientBehavior( ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime )
{
clientRuntime.ClientMessageInspectors.Add( new DispatchInspector() );
}
public void ApplyDispatchBehavior( ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher )
{
}
public void Validate( ServiceEndpoint endpoint )
{
}
#endregion
}
class DispatchInspector : IClientMessageInspector
{
#region IClientMessageInspector Members
public void AfterReceiveReply( ref Message reply, object correlationState )
{
MessageBuffer buffer = reply.CreateBufferedCopy( Int32.MaxValue );
reply = buffer.CreateMessage();
Console.WriteLine( "Receiving:\n{0}", buffer.CreateMessage().ToString() );
}
public object BeforeSendRequest( ref Message request, IClientChannel channel )
{
MessageBuffer buffer = request.CreateBufferedCopy( Int32.MaxValue );
request = buffer.CreateMessage();
Console.WriteLine( "Sending:\n{0}", buffer.CreateMessage().ToString() );
return null;
}
#endregion
}
There is no answer to which is better, however:
if you don't need anything but the default behavior - you can stick with the ChannelFactory, it's the simplest of the two
for anything extra, use the ClientBase, it requires an extra class, though

Related

How to read WCF message headers in duplex callback?

In a normal WCF request/reply contract, you can read the message headers using something like:
OperationContract.Current.IncomingMessageHeaders
What I can't figure out is how to do this on the callback side of a duplex contract. Inside the callback implementation OperationContext.Current is null.
Edit 4/5/2013:
I'm using a custom binding based on net.tcp, but with a lot of customizations. For example, using protocol buffers message encoding rather than Xml. Also there is some custom security.
What binding are you using? In the SSCCE below the context is not null on the callback implementation.
public class StackOverflow_15769719
{
[ServiceContract(CallbackContract = typeof(ICallback))]
public interface ITest
{
[OperationContract]
string Hello(string text);
}
[ServiceContract]
public interface ICallback
{
[OperationContract(IsOneWay = true)]
void OnHello(string text);
}
public class Service : ITest
{
public string Hello(string text)
{
ICallback callback = OperationContext.Current.GetCallbackChannel<ICallback>();
ThreadPool.QueueUserWorkItem(delegate
{
callback.OnHello(text);
});
return text;
}
}
class MyCallback : ICallback
{
AutoResetEvent evt;
public MyCallback(AutoResetEvent evt)
{
this.evt = evt;
}
public void OnHello(string text)
{
Console.WriteLine("[callback] Headers: ");
foreach (var header in OperationContext.Current.IncomingMessageHeaders)
{
Console.WriteLine("[callback] {0}", header);
}
Console.WriteLine("[callback] OnHello({0})", text);
evt.Set();
}
}
public static void Test()
{
bool useTcp = false;
string baseAddress = useTcp ?
"net.tcp://" + Environment.MachineName + ":8000/Service" :
"http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
Binding binding = useTcp ?
(Binding)new NetTcpBinding(SecurityMode.None) :
new WSDualHttpBinding(WSDualHttpSecurityMode.None)
{
ClientBaseAddress = new Uri("http://" + Environment.MachineName + ":8888/Client")
};
host.AddServiceEndpoint(typeof(ITest), binding, "");
host.Open();
Console.WriteLine("Host opened");
AutoResetEvent evt = new AutoResetEvent(false);
MyCallback callback = new MyCallback(evt);
DuplexChannelFactory<ITest> factory = new DuplexChannelFactory<ITest>(
new InstanceContext(callback),
binding,
new EndpointAddress(baseAddress));
ITest proxy = factory.CreateChannel();
Console.WriteLine(proxy.Hello("foo bar"));
evt.WaitOne();
((IClientChannel)proxy).Close();
factory.Close();
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}

Attributes on a derived type not being deserialized in a WCF client even though KnownType is used

I have the following types:
public enum MyEnum
{
Value1,
Value2
}
[DataContract]
public class Configuration
{
[DataMember]
public MyEnum MyValue { get; set; }
[DataMember]
public Credentials CredentialValues { get; set; }
}
[DataContract, KnownType(typeof(CustomCredentials))]
public class Credentials
{
}
[DataContract]
public class CustomCredentials : Credentials
{
[DataMember]
public string Property1 { get; set; }
[DataMember]
public string Property2 { get; set; }
}
And on my service interface, I have a function that returns an instance of Configuration with its CredentialValues property set to a fully populated instance of CustomCredentials. I receive no errors from the client or the server, but while the data is being property serialized on the server and received by the client, the properties on CustomCredentials never have a value. What do I need to change here in order to have these properties properly deserialized on the client?
For reference, the connection between client and server is made with a DuplexChannelFactory over a NetTcpBinding using a data/service contract project that is shared by the client and service applications (the service is self-hosted), so there are no service proxy types that could need to be regenerated.
Added this code to the Contracts project along with your DataContracts.
[ServiceContract(Namespace = "http://schemas.platinumray.com/duplex", SessionMode = SessionMode.Required, CallbackContract = typeof(IService1Callback))]
public interface IService1
{
[OperationContract(IsOneWay = true)]
void GetData();
}
public interface IService1Callback
{
[OperationContract(IsOneWay = true)]
void SetData(Configuration config);
}
Created the service.
public class Service1 : IService1
{
public void GetData()
{
var x = new Configuration()
{
MyValue = MyEnum.Value1,
CredentialValues = new CustomCredentials { Property1 = "Something", Property2 = "Something else" }
};
OperationContext.Current.GetCallbackChannel<IService1Callback>().SetData(x);
}
}
class Program
{
static void Main(string[] args)
{
using (ServiceHost host = new ServiceHost( typeof(Service1), new Uri[] { new Uri("net.tcp://localhost:6789") }))
{
host.AddServiceEndpoint(typeof(IService1), new NetTcpBinding(), "Service1");
host.Open();
Console.ReadLine();
host.Close();
}
}
}
Created the client.
public class CallbackHandler : IService1Callback
{
public void SetData(Configuration config)
{
Console.WriteLine(config.CredentialValues.GetType().Name);
Console.WriteLine(((CustomCredentials)config.CredentialValues).Property1);
Console.WriteLine(((CustomCredentials)config.CredentialValues).Property2);
}
}
class Program
{
static void Main(string[] args)
{
// Setup the client
var callbacks = new CallbackHandler();
var endpoint = new EndpointAddress(new Uri("net.tcp://localhost:6789/Service1"));
using (var factory = new DuplexChannelFactory<IService1>(callbacks, new NetTcpBinding(), endpoint))
{
var client = factory.CreateChannel();
client.GetData();
Console.ReadLine();
factory.Close();
}
}
}
Outputs the following as expected:
CustomCredentials
Something
Something else
So this actually worked without modifying any of your data contracts... The same results if I revert to a twoway operation and just return Configuration directly without using the callback.
Also tried making Credentials abstract but could not replicate your problem.
Have I missed something?

What's the best way to serialize an array based on an interface in WCF?

First the code:
[ServiceContract]
public interface IWorker
{
[OperationContract]
void Process(XmlElement data);
[OperationContract]
void Update(Rule rule);
}
[DataContract]
public class Rule
{
[OperationContract]
public string Expression { get; set; }
[OperationContract]
public List<IAction> Actions { get; set; }
}
public interface IAction
{
void Execute(XmlElement data);
}
A dispatcher encodes data as xml and sends it to an IWorker instance where each expression is evaluated. When an IWorker instance evaluates an expression as true, IAction.Execute is called and the xml/data is passed.
What's the best way to serialize Rule.Actions? I've started writing a custom serializer but I'd prefer to see if there is an easier way.
Thanks.
I dont think you can use interfaces in DataContracts (someone correct me if im wrong, but i assume thats like trying to use a generic too). What I do, is have a parent class, then add the KnownType attribute. For instance
[DataContract]
public class Action
{
//members and properties
}
[DataContract]
public class SomeOtherAction:Action
{
//more implimentation
}
[DataContract]
[KnownType(typeof(SomeOtherAction))]
public class Rule
{
[DataMember]
List<Action> Actions{get;set;}
}
Now you can stuff any object that inherits from the parent Action object in to the Actions list, and it will properly serialize all their respective class properties (as long as the object is listed as a knowntype).
*I used "Action" name as an example to relate to yours, obviously Action is a keyword in .NET
Serialization is the process of converting between an object data and bytes which can be transferred over the wire. Interfaces define behavior, so by default WCF can't serialize such data. If you have the exact same assemblies on the client and the server, however, you can use the NetDataContractSerializer, which will essentially serialize (and be able to serialize) all the type information for the objects being serialized, so it can be recreated at the other side.
The code below shows how to use the NetDataContractSerializer in a service for that (based on the main example for this, the post from Aaron Skonnard at http://www.pluralsight-training.net/community/blogs/aaron/archive/2006/04/21/22284.aspx)
public class StackOverflow_6932356
{
[ServiceContract]
public interface IWorker
{
[OperationContract]
void Process(XmlElement data);
[OperationContract]
void Update(Rule rule);
}
[DataContract]
public class Rule
{
[DataMember]
public string Expression { get; set; }
[DataMember]
public List<IAction> Actions { get; set; }
}
public interface IAction
{
void Execute(XmlElement data);
}
public class Service : IWorker
{
static List<IAction> AllActions = new List<IAction>();
public void Process(XmlElement data)
{
foreach (var action in AllActions)
{
action.Execute(data);
}
}
public void Update(Rule rule)
{
AllActions = rule.Actions;
}
}
public class Action1 : IAction
{
public void Execute(XmlElement data)
{
Console.WriteLine("Executing {0} for data: {1}", this.GetType().Name, data.OuterXml);
}
}
public class Action2 : IAction
{
public void Execute(XmlElement data)
{
Console.WriteLine("Executing {0} for data: {1}", this.GetType().Name, data.OuterXml);
}
}
class NetDataContractSerializerOperationBehavior : DataContractSerializerOperationBehavior
{
public NetDataContractSerializerOperationBehavior(OperationDescription operationDescription)
: base(operationDescription) { }
public override XmlObjectSerializer CreateSerializer(Type type, string name, string ns, IList<Type> knownTypes)
{
return new NetDataContractSerializer(name, ns);
}
public override XmlObjectSerializer CreateSerializer(Type type, XmlDictionaryString name, XmlDictionaryString ns, IList<Type> knownTypes)
{
return new NetDataContractSerializer(name, ns);
}
}
static void ReplaceDCSOB(ServiceEndpoint endpoint)
{
foreach (var operation in endpoint.Contract.Operations)
{
for (int i = 0; i < operation.Behaviors.Count; i++)
{
if (operation.Behaviors[i] is DataContractSerializerOperationBehavior)
{
operation.Behaviors[i] = new NetDataContractSerializerOperationBehavior(operation);
break;
}
}
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
ServiceEndpoint endpoint = host.AddServiceEndpoint(typeof(IWorker), new BasicHttpBinding(), "");
ReplaceDCSOB(endpoint);
host.Open();
Console.WriteLine("Host opened");
var factory = new ChannelFactory<IWorker>(new BasicHttpBinding(), new EndpointAddress(baseAddress));
ReplaceDCSOB(factory.Endpoint);
var proxy = factory.CreateChannel();
proxy.Update(new Rule
{
Expression = "Expr",
Actions = new List<IAction> { new Action1(), new Action2() }
});
XmlDocument doc = new XmlDocument();
doc.LoadXml("<root><foo>bar</foo></root>");
proxy.Process(doc.DocumentElement);
((IClientChannel)proxy).Close();
factory.Close();
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}

How can I programmatically get the binding that my client proxy is using?

I have a WCF proxy generated at runtime with DuplexChannelFactory.
How can I access the binding information given only the service interface returned from DuplexChannelFactory?
I can get most stuff by casting to an IClientChannel, but I can't seem to find binding info on there. The closest I can get is IClientChannel.RemoteAddress which is an endpoint, but that doesn't seem to have binding info either. :-/
You can't (directly). There are a few things which you can get from the channel, such as the message version (channel.GetProperty<MessageVersion>()), and other values. But the binding isn't one of those. The channel is created after the binding is "deconstructed" (i.e., expanded into its binding elements, while each binding element can add one more piece to the channel stack.
If you want to have the binding information in the proxy channel, however, you can add it yourself, using one of the extension properties of the context channel. The code below shows one example of that.
public class StackOverflow_6332575
{
[ServiceContract]
public interface ITest
{
[OperationContract]
int Add(int x, int y);
}
public class Service : ITest
{
public int Add(int x, int y)
{
return x + y;
}
}
static Binding GetBinding()
{
BasicHttpBinding result = new BasicHttpBinding();
return result;
}
class MyExtension : IExtension<IContextChannel>
{
public void Attach(IContextChannel owner)
{
}
public void Detach(IContextChannel owner)
{
}
public Binding Binding { get; set; }
}
static void CallProxy(ITest proxy)
{
Console.WriteLine(proxy.Add(3, 5));
MyExtension extension = ((IClientChannel)proxy).Extensions.Find<MyExtension>();
if (extension != null)
{
Console.WriteLine("Binding: {0}", extension.Binding);
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
host.AddServiceEndpoint(typeof(ITest), GetBinding(), "");
host.Open();
Console.WriteLine("Host opened");
ChannelFactory<ITest> factory = new ChannelFactory<ITest>(GetBinding(), new EndpointAddress(baseAddress));
ITest proxy = factory.CreateChannel();
((IClientChannel)proxy).Extensions.Add(new MyExtension { Binding = factory.Endpoint.Binding });
CallProxy(proxy);
((IClientChannel)proxy).Close();
factory.Close();
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}

WCF shared IClientMessageInspector instance across multiple clients

I'm managing a shared auth cookie when making WCF service calls via this methodology outlined under the header "Centralized cookie management" located here: http://megakemp.com/2009/02/06/managing-shared-cookies-in-wcf/
I've set up a custom IClientMessageInspector, IEndpointBehavior, BehaviorExtensionElement, the works. My endpoint behavior adds a message inspector as follows:
public class MyEndpointBehavior : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
// yuck.. Wish I had an instance of MyClientMessageInspector
// (which has the auth cookie already) so I could just inject that
// instance here instead of creating a new instance
clientRuntime.MessageInspectors.Add(new MyClientMessageInspector());
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
}
public void Validate(ServiceEndpoint endpoint)
{
}
}
It all works flawlessly, but this solution breaks down when you want to share cookies over multiple clients. Because the ApplyDispatchBehavior() method creates a new instance, any other client wouldn't get that message inspector instance, and thus, the auth ticket.
So then I thought of trying to create a custom constructor where I could inject the instance like so:
MyEndpointBehavior(MyClientMessageInspector msgInspector) { ... }
But, WCF needs parameter-less constructors. Weeding through the internets, WCF has hooks to allow for dependency injection, creating an IInstanceProvider, IServiceBehavior, etc. But I don't think that's what I'm looking for here.
Can anyone help guide me in the right direction?
You need only extend the concept so that you store the cookie outside of the message inspector itself so that all instances of the message inspector share the same storage.
The poor man's way, just to get started, would be to just use a static field instead of an instance field. Obviously if you have multiple threads you'll need to provide concurrency while updating the field. From there you can get even fancier if you extrapolate it out to a cookie container concept and then just make sure you share the same container with all clients. Sharing the container can be done by getting the ChannelParameterCollection for the client channel and adding property to it and then your behavior looks for that property while it's inspecting the mssage and pulling the cookies out of that. That would look a little something like this:
App logic
// Hold onto a static cookie container
public static CookieContainer MyCookieContainer;
// When instantiating the client add the cookie container to the channel parameters
MyClient client = new MyClient();
client.InnerChannel.GetProperty<ChannelParameterCollection>().Add(MyCookieContainer);
Message inspector logic
public void BeforeSendMessage(ref Message, IClientChannel clientChannel)
{
// Find the cookie container for the current channel
CookieContainer cookieContainer = clientChannel.GetProperty<ChannelParameterCollection>().Select(p => p as CookieContainer).Where(cc => cc != null).First();
// ... use the cookie container to set header on outgoing context ...
}
You're correct, IInstanceProvider won't help in your case - it's used for providing service instances only. You don't need a parameterless constructor for your behavior. You need a paramterless constructor for the config element, and this class can use some dependency injection class (see below) to create the appropriate inspector class needed for the behavior.
namespace ConsoleApplication4
{
public class MyEndpointBehavior : IEndpointBehavior
{
IClientMessageInspector inspector;
public MyEndpointBehavior(IClientMessageInspector inspector)
{
this.inspector = inspector;
}
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.MessageInspectors.Add(this.inspector);
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
}
public void Validate(ServiceEndpoint endpoint)
{
}
}
public class MyEndpointBehaviorElement : BehaviorExtensionElement
{
public override Type BehaviorType
{
get { return typeof(MyEndpointBehavior); }
}
protected override object CreateBehavior()
{
return new MyEndpointBehavior(ClientInspectorFactory.GetClientInspector());
}
}
public class MyClientInspector : IClientMessageInspector
{
public MyClientInspector()
{
}
public void AfterReceiveReply(ref Message reply, object correlationState)
{
Console.WriteLine("AfterReceiveReply");
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
Console.WriteLine("BeforeSendRequest");
return null;
}
}
public static class ClientInspectorFactory
{
static IClientMessageInspector instance;
public static IClientMessageInspector GetClientInspector()
{
if (instance == null)
{
instance = new MyClientInspector();
}
return instance;
}
}
[ServiceContract]
public interface ITest
{
[OperationContract]
int Add(int x, int y);
}
public class Service : ITest
{
public int Add(int x, int y) { return x + y; }
}
class Program
{
static void Main(string[] args)
{
ServiceHost host = new ServiceHost(typeof(Service));
host.Open();
Console.WriteLine("Host opened");
ChannelFactory<ITest> factory = new ChannelFactory<ITest>("client1");
ITest proxy = factory.CreateChannel();
Console.WriteLine(proxy.Add(3, 4));
((IClientChannel)proxy).Close();
factory.Close();
factory = new ChannelFactory<ITest>("client2");
proxy = factory.CreateChannel();
Console.WriteLine(proxy.Add(5, 8));
((IClientChannel)proxy).Close();
factory.Close();
host.Close();
}
}
}
I liked the answers provided by #carlosfigueira and #drew, but I ultimately came up with a slightly different approach. I opted to configure my IEndpointBehavior PROGRAMMATICALLY, vs via config. Made things much simpler. I changed my endpoint behavior to store my client message inspector as follows:
public class MyEndpointBehavior : IEndpointBehavior
{
private MyClientMessageInspector_myClientMessageInspector;
public MyClientMessageInspector MyClientMessageInspector
{
get
{
if (_myClientMessageInspector == null)
{
_myClientMessageInspector = new MyClientMessageInspector();
}
return _myClientMessageInspector;
}
}
public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
clientRuntime.MessageInspectors.Add(MyClientMessageInspector);
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
}
public void Validate(ServiceEndpoint endpoint)
{
}
}
Then I simply shared this behavior between clients, as follows:
var behavior = new MyEndpointBehavior();
client1.Endpoint.Behaviors.Add(behavior);
client2.Endpoint.Behaviors.Add(behavior);
Now both clients will share the same auth cookie.