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

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?

Related

Ninject Dependency Binding to WCF Services Without XML Configuration

I am trying to use the Ninject Dependency Injection to bind a callback method to WCF REST services runs in a kind of plugin module of a software system, which is NOT possible to use the SVC file or webconfig or app.config for any configuration.
The interface and implementation of the WCF services are defined as below:
[ServiceContract]
public interface IService1
{
[OperationContract]
[WebInvoke(Method = "GET", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
string DoWork();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class Service1 : IService1
{
private IEventCallback EventCallback { get; set; }
public Service1(IEventCallback eventCallback)
{
EventCallback = eventCallback;
}
public string DoWork()
{
if (EventCallback != null)
{
EventCallback.Send("Testing Event ID");
}
return "Success";
}
}
where the IEventCallback and the corresponding implementation are defined as below:
public interface IEventCallback
{
void Send(string eventId);
}
public class EventCallback : IEventCallback
{
private Action<string> OnSendCustomEventCallBack { get; set; }
public EventCallback(Action<string> onSendCustomEventCallBack)
{
OnSendCustomEventCallBack = onSendCustomEventCallBack;
}
public void Send(string eventId)
{
if (OnSendCustomEventCallBack != null)
{
OnSendCustomEventCallBack(eventId);
}
}
}
The codes to create the REST Service are as below:
public AuthenticatedWebServiceHost(Type type, Uri url, string authenUsername, string authenPassword)
{
AuthenUsername = authenUsername;
AuthenPassword = authenPassword;
IDictionary<string, ContractDescription> desc;
InitializeDescription(type, new UriSchemeKeyedCollection());
base.CreateDescription(out desc);
var val = desc.Values.First();
var binding = new WebHttpBinding();
binding.Security.Mode = WebHttpSecurityMode.TransportCredentialOnly;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
Credentials.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom;
Credentials.UserNameAuthentication.CustomUserNamePasswordValidator =
new CustomUserNamePasswordValidator(AuthenUsername, AuthenPassword);
AddServiceEndpoint(val.ContractType, binding, url);
}
And, the AuthenticatedWebServiceHost is called as below:
var eventCallback = new EventCallback(OnSendCustomEventCallBack); // where OnSendCustomEventCallBack is a defined method
// How to write codes to use Ninject to inject the callback into the Service?
// kernel.Bind<IEventCallback>().To<??>()
_webServiceHost = new AuthenticatedWebServiceHost(typeof(Service1), new Uri("http://localhost:9000/Events"),
"admin", "password");
_webServiceHost.Open();
Since, no XML configuration is allowed in my case, how to write codes to use Ninject to bind the callback to the WCF Services?
I eventually figured out the solution by referencing Is it possible to instantiate a WebServiceHost via an instance of the service type, without a parameterless constructor? without using NInject.

WCF Inherit contracts and callback contract

I have a test project that test the Inherit contracts and callback contract.
Update 1 : I updated all of the topic with interfaces implamantations
By reading this article: http://codeidol.com/csharp/wcf/Service-Contracts/Contract-Inheritance/#part-16
This is possibol but when i try it, it fails.
class Program
{
static void Main(string[] args)
{
ServiceHost Service_IServer = new ServiceHost(new ServiceImplemantation(), new Uri(#"net.tcp://localhost:8080/"));
Service_IServer.AddServiceEndpoint(typeof(IService), new NetTcpBinding(), "Service");
Service_IServer.Open();
ServiceHost Service_I_IP = new ServiceHost(new IPImplemantation(), new Uri(#"net.tcp://localhost:8080/"));
Service_I_IP.AddServiceEndpoint(typeof(I_IP), new NetTcpBinding(), "Service");
Service_I_IP.Open();
Console.ReadLine();
}
}
[ServiceContract]
public interface I_IP
{
[OperationContract]
string GetIP();
}
[ServiceContract]
public interface IService : I_IP
{
[OperationContract]
void ImTheServer_Print();
}
//
//
//
//
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class IPImplemantation : I_IP
{
public string GetIP()
{
return "1.2.3.4";
}
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class ServiceImplemantation : IPImplemantation, IService
{
public void ImTheServer_Print()
{
Console.WriteLine("ImTheServer_Print");
}
}
The error:
The ChannelDispatcher at 'net.tcp://localhost:8080/Service' with contract(s) '"I_IP"' is unable to open its IChannelListener.
Ensure you decorate it as OneWay.
[OperationContract(IsOneWay = true)]
void GetIP();

wcf rest returns object but properties are null

Just trying to get a small demo working voor wcf rest. I have a domain classlibrary, a console application and a wcf service project.
The interface [ServiceContract] is located in the Domain classlib:
[ServiceContract]
public interface IService1
{
[OperationContract]
[WebGet]
CompositeType GetDataUsingDataContract(string id);
}
The implementation is located in the wcf service application (visual studio template):
[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
public class Service1 : IService1
{
public CompositeType GetDataUsingDataContract(string id)
{
if (id != null)
{
return new CompositeType() { StringValue = id, BoolValue=true };
}
return null;
}
}
When I bind this in IIS properly it works just fine:
http://test123/Service1.svc/GetDataUsingDataContract?id=42
outputs:
{"d":{"__type":"CompositeType:#Domain","BoolValue":true,"StringValue":"42"}}
I then wrote the consumer:
static void Main(string[] args)
{
var factory = new ChannelFactory<IService1>(new WebHttpBinding(), new EndpointAddress("http://test123/Service1.svc"));
factory.Endpoint.Behaviors.Add(new WebHttpBehavior());
var service = factory.CreateChannel();
CompositeType result = service.GetDataUsingDataContract("42");
}
This compiles and runs, but the properties of CompositeType result are null and false. Serialization didn't go properly. I used fiddler to verify that the http call is correct and it is exactly as I do manually with the browser.
When I change the parameter "42" to null, it also gives me a CompositeType object instead of null. Fiddler shows {"d":null} as expected.
I'm clueless!
Update
The code for CompositeType
[DataContract]
public class CompositeType
{
[DataMember]
public bool BoolValue { get; set; }
[DataMember]
public string StringValue { get; set; }
}

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();
}
}

Get the domain name of a WCF request?

How can I obtain the domain name or full URL of the requester?
I'm not sure that I understand your question, but if you need the domain name of the Windows user making the call to a service operation, use this:
OperationContext.Current.ServiceSecurityContext.PrimaryIdentity.Name
This will return "{domain}\{username}".
Try this and let me know what you think (you'll probably want to paste this code into an mstest project):
[TestClass]
public class AlternativeCredentials
{
// Contracts
[ServiceContract]
interface IMyContract
{
[OperationContract]
string GetUserName();
}
// Service
[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
class MyService : IMyContract
{
public string GetUserName()
{
return OperationContext.Current.ServiceSecurityContext.PrimaryIdentity.Name;
}
}
// Client
class MyContractClient : ClientBase<IMyContract>, IMyContract
{
public MyContractClient() { }
public MyContractClient(Binding binding, string address) :
base(binding, new EndpointAddress(address)) { }
public string GetUserName()
{ return Channel.GetUserName(); }
}
#region Host
static string address = "net.tcp://localhost:8001/" + Guid.NewGuid().ToString();
static ServiceHost host;
[ClassInitialize()]
public static void MyClassInitialize(TestContext testContext)
{
host = new ServiceHost(typeof(MyService));
host.AddServiceEndpoint(typeof(IMyContract), new NetTcpBinding(), address);
host.Open();
}
[ClassCleanup()]
public static void MyClassCleanup()
{
if (host.State == CommunicationState.Opened)
host.Close();
}
#endregion
[TestMethod]
public void UseUserNameCredentials()
{
using (MyContractClient proxy =
new MyContractClient(new NetTcpBinding(), address))
{
proxy.ClientCredentials.UserName.UserName = "MyUsername";
proxy.ClientCredentials.UserName.Password = "MyPassword";
proxy.Open();
Assert.AreEqual("EMS\\magood", proxy.GetUserName());
proxy.Close();
}
}
}