How To Use Interface as DataContract in WCF - c#

I need invoke webservice operations using standard wsdl, but data objects must be different in client and in the server.
Using interfaces for data objects in a common library, making proxy classes for it in client and in server.
Then, I'm declaring operation contract using the interface, but WCF don't recognize it.
I yet tried use DataContractSerializerBehavior and set knownTypes, no success yet.
Someone can help-me? I've attached a complete solution with more details.
public interface Thing
{
Guid Id {get;set;}
String name {get;set;}
Thing anotherThing {get;set;}
}
[DataContract]
public class ThingAtServer: BsonDocument, Thing // MongoDB persistence
{
[DataMember]
Guid Id {get;set;}
//...
}
[DataContract]
public class ThingAtClient: Thing, INotifyPropertyChanged // WPF bindings
{
[DataMember]
Guid Id {get;set;}
//...
}
[ServiceContract]
public interface MyService
{
[OperationContract]
Thing doSomething(Thing input);
}
Click here do see a Sample project on GitHub with TestCases

I've created WCF Service with contract:
[OperationContract]
CompositeTypeServer GetDataUsingDataContract( CompositeTypeServer composite );
My CompositeTypeServer looks like this:
[DataContract( Namespace = "http://enes.com/" )]
public class CompositeTypeServer
{
[DataMember]
public bool BoolValue { get; set; }
[DataMember]
public string StringValue { get; set; }
}
Then I've created client project with type CompositeTypeClient:
[DataContract( Namespace = "http://enes.com/" )]
public class CompositeTypeClient
{
[DataMember]
public bool BoolValue { get; set; }
[DataMember]
public string StringValue { get; set; }
}
Then I've added the reference to my service and selected to reuse types. Everything worked like charm. I was able to use CompositeTypeClient on client side.
So the trick was to specify Namespace for DataContract so they would match on both client and service.
[DataContract( Namespace = "http://enes.com/" )]
PS. I can provide full working VS solution on request.

Based on ServiceKnownTypeAttribute (MSDN documentation), I changed what types expected depending on the situation. The main idea is implemented in the class XHelper, responsible to return the correct Type[] according to the situation:
public static class XHelper
{
public static Boolean? IsClient = null;
public static Type[] ClientTypes;
public static Type[] ServerTypes;
public static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider pProvider)
{
if (!IsClient.HasValue)
throw new Exception("Invalid value");
if (IsClient.Value)
return ClientTypes;
return ServerTypes;
}
}
You must include the ServiceKnownType tag in the interface that has the ServiceContract to know XHelper class.
[ServiceContract(Namespace = MyProxyProvider.MyNamespace)]
[ServiceKnownType("GetKnownTypes", typeof(XHelper))]
public interface MyService
{
[OperationContract]
Thing2 CopyThing(Thing1 input);
}
At the beginning of the test unit, which was informed of the right Type[] for every situation:
[AssemblyInitialize]
public static void TestInitialize(TestContext pContext)
{
XHelper.ClientTypes = new Type[] { typeof(Thing1ProxyAtClient), typeof(Thing2ProxyAtClient), typeof(Thing2ProxyAtClient) };
XHelper.ServerTypes = new Type[] { typeof(Thing1ProxyAtServer), typeof(Thing2ProxyAtServer), typeof(ThingNProxyAtServer) };
}
Click here do see the final code Sample project on GitHub with TestCases

Related

ServiceStack: Property in request DTO becomes null if type is abstract

I have a ServiceStack 3-based client-server architecture. I'm trying to create a service whose request DTO contains a property with an abstract type, with two different concrete classes implementing it. The abstract type could be either an abstract class or an interface; however, in either case, the server receives a null object in the property.
There's three assemblies and corresponding namespaces: TestClient, Server, and CommonLib referenced by both client and server.
That is, spread across the three assemblies:
namespace CommonLib.Services
{
public class GetThing : IReturn<GetThingResponse> // request DTO
{
public IThisOrThat Context { get; set; }
}
public class GetThingResponse
{
public Dictionary<int, string> Result { get; private set; }
public GetThingResponse(Dictionary<int, string> result) // response DTO
{
Result = result;
}
}
}
namespace CommonLib
{
public interface IThisOrThat { }
public class This : IThisOrThat { } // and so forth
}
namespace Server.Services
{
public class GetThing Service : IService
{
public object Get(GetThing request)
{
var foo = request.Context; // this is null
}
}
}
namespace TestClient
{
class Program
{
public const string WSURL = "http://localhost:61435/";
static void Main(string[] args)
{
using (var client = new JsonServiceClient(WSURL))
{
var result = client.Get(new GetThing
{
Context = new CommonLib.This("context info")
});
}
}
If I change the Context property in GetThing to be of type This instead of IThisOrThat, this works. Leaving it as the interface, or changing IThisOrThat to be an abstract class, results in the data being transmitted as null.
I'm assuming this is a serialization problem. I've tried changing the interface to an abstract class and decorating that with appropriate KnownType attributes, but ServiceStack's serializer doesn't appear to benefit from this. Is there any trick to get this done?
You would need to enable JsConfig.IncludeTypeInfo = true; on the client side, so the serializer includes the type information with the request. This will add an extra property (__type) with the type definition so the service knows what to type it as.
It fails currently because requests by default don't provide type information to deserialize the object into the class that implements the interface. This was an issue that was previously raised.
The problem is the when the JSON client makes the request, it will serialize up the a class that implements IThisOrThat such as your This class. But when it gets to the other end ServiceStack.Text doesn't know what to deserialize the object into. The type information is lost so it doesn't know what kind of IThisOrThat it is. So without the additional __type information property in the request this is happening:
Scenario:
interface ISomething
{
string Name;
}
class MySomething : ISomething
{
public string Name { get; set; }
public int Age { get; set; }
}
class MySomethingElse : ISomething
{
public string Name { get; set; }
public int Size { get; set; }
}
Then you make the call from your JsonServiceClient using a typed object
client.Get(new MySomething { Name: "Duck", Age: 20 });
The JSON that is sent would be { "Name":"Duck", "Age":20 } what type does the deserialiser choose now? It could be an MySomething or a MySomethingElse, or even another ISomething that it just doesn't know about yet. So because it can't decide the result is simply null.
Generally interfaces and DTOs don't mix, see here.
I had a similar problem, and realized i didn't have { get; set; } applied to the response DTO, so the result of my object was always null...
Thought this information could also help anyone searching for this ...

WCF simple serialization

I have a common class (common meaning it is defined it its own library, used by both the client and service)
[Serializable]
Class C1
{
string s1;
string s2;
}
in my WCF service I create an instance, populate and return it:
this is the interface:
[ServiceContract]
public interface myInterface
{
[OperationContract]
c1 GetClass(string number);
}
here is my service class:
public class ColorTracker : myInterface
{
public c1 GetObj(string value)
{
c1 theColor = new c1();
c1.s1= "value1";
c1.s2= "value2";
return c1;
}
}
client:
using (ServieClient bmClient = new ServiceClient())
{
c1 theColor;
theColor = (c1) bmClient.GetObj("test");
}
It all kind of works, but I can only access the return data through the backing fields. I would like to instantiate the object (or i suppose it could be a structure since its only data, not state)
Thanks in advance.
WCF does not care about [Serializable] attribute. If you want you class to be a data contract, you should mark it with the [DataContract] attribute instead.
Also, convert your fields to properties, and mark them with the [DataMember] attribute.
[DataContract]
Class C1
{
[DataMember]
public string s1 {get;set;}
[DataMember]
public string s2 {get;set;}
}
When your attribute is [Serializable] then in the client side you can just use private fields! but,since your properties are Auto genenrated fiels {get;set;} ,in the client side you can see auto generated prop__BackingFields;
you can Refactor your properties like this:
[Serializable ]
public class MyData
{
private string _prop1;
public string Prop1
{
get { return _prop1; }
set { _prop1 = value; }
}
but I think the best way to code your issue is this:
1.Define two data contracts:
[DataContract ]
public class ServerSideFilledData
{...}and
[DataContract ]
public class ClientSideFilledData
{...}
2.Define a Message Contract with two MessageBody Attributes:
[MessageContract]
public class MixedServerandClientSideData
{
private ClientSideFilledData clientSideFilledData;
[MessageBodyMember]
public ClientSideFilledData ClientSideFilledData
{
get { return clientSideFilledData; }
set { clientSideFilledData = value; }
}
private ClientSideFilledData serverSideFilledData;
[MessageBodyMember]
public ClientSideFilledData ServerSideFilledData
{
get { return serverSideFilledData; }
set { serverSideFilledData = value; }
}
}
3.Instead of Serializable Or DataContract, use your Message for exchanging data between cient and server
[ServiceContract]
public interface IService1
{
[OperationContract]
MixedServerandClientSideData DoWork();
}
4.I think you know the continue ,initialize and fill your data in convenient sides.

Protobuf-net not serializing base class members

We have the following classes and WCF service (using protobuf-net for serialization):
[DataContract]
[KnownType(typeof(NamedViewModel))]
public class NamedViewModel<TKey> : IViewModel
{
[DataMember]
public virtual TKey Id { get; set; }
[DataMember]
public virtual string Name { get; set; }
}
[DataContract]
[KnownType(typeof(ScheduleTemplateViewModel))]
public class NamedViewModel : NamedViewModel<int>
{
}
[DataContract]
public class ScheduleTemplateViewModel : NamedViewModel
{
[DataMember]
public string Comment { get; set; }
}
[DataContract]
public class Container
{
[DataMember]
public IEnumerable<ScheduleTemplateViewModel> Templates { get; set; }
}
[ServiceContract]
public interface IService
{
[OperationContract]
Container Get();
}
public class Service : IService
{
public IEnumerable<Container> Get()
{
return new Container { Templates = Enumerable.Range(1, 10)
.Select(i => CreateTemplate()).ToArray() };
}
private void ScheduleTemplateViewModel CreateTemplate()
{
var instance = WindsorContainer.Resolve<ScheduleTemplateViewModel>();
// populate instance
return instance;
}
}
We have two problems:
We get an exception during serialization that the Castle DynamicProxy type for ScheduleTemplateViewModel is unexpected. We noticed that there is custom code in protobuf-net to handle NHibernate and EntityFramework proxies...but not Castle DynamicProxies. We worked around this by adding an additional case statement in the protobuf-net source code to check for Castle's IProxyTargetAccessor type...but it would be nice if there were a way of handling this without modifying the protobuf-net source code...
Members on ScheduleTemplateViewModel (namely Comment) are serialized correctly...but base class Members are not. We already have the InferTagFromNameDefault set to true on RuntimeTypeModel.Default.
I can add that; can you tell me the full name (including namespace) of that interface?
From the example you give, none of those values should serialize, as none of them include the necessary numeric field-number information. Since you say some do serialize, I'm going to assume that this is an omission in the copy/paste. Protobuf-net will try to use the Order=n information from [DataMember(...)] if nothing better is available. However, if must be emphasized that protobuf-net cannot use [KnownType(...)], and inheritance again needs some explicit numeric field-number information. This is most easily added via [ProtoInclude(...)], but can also be provided at runtime

c# Passing a struct in a WCF method

I want to pass a class to a client over a WCF service. In that class I use a struct. But the value I receive at client side is: "System.Data.DataSet"
Must be something I don't understand.
See my struct (it's just a string for now)
namespace spine.datatypes
{
[Serializable]
public struct Tanga : IXmlSerializable
{
private string _value;
public Tanga(string value)
{
this._value = value;
}
public static implicit operator Tanga(string value)
{
return new Tanga(value);
}
public override string ToString()
{
return this._value;
}
// implement IXmlSerializable
public XmlSchema GetSchema() { return null; }
public void ReadXml(XmlReader reader)
{
_value = reader.ReadContentAsString();
}
public void WriteXml(XmlWriter writer)
{
writer.WriteValue(this._value.ToString());
}
}
}
This is my service:
namespace webapplication.WCFservice.Recorder
{
[ServiceContract]
[XmlSerializerFormat]
public interface IWCFRecorder
{
[OperationContract]
TvRecorder getDedicatedJob(String recordername, String recorderip);
}
}
And this is the class I pass:
namespace spine.recorder.tv
{
[Serializable()]
[XmlRoot("Recorder")]
public class TvRecorder
{
public int id { get; set; }
public Tanga name { get; set; }
public MyIpAddress ip { get; set; }
public int channel { get; set; }
public MyTimecode time_start { get; set; }
public MyTimecode duration { get; set; }
public TvRecorder() { }
public TvRecorder(int _id, Tanga _name, MyIpAddress _ip, int _channel, MyTimecode _time_start, MyTimecode _duration)
{
this.id = _id;
this.name = _name;
this.ip = _ip;
this.channel = _channel;
this.time_start = _time_start;
this.duration = _duration;
}
}
}
There are unfortunately cases where svcutil generates a DataContract type and an XmlSerializer type for the same schema type. I suggest you try using the additional “/serializer:XmlSerializer /useSerializerForFaults” switches to svcutil and see if that resolves your issue. It should ensure that Tanga gets generated.
In general, for schema import to generate DataContract types, all of the types defined in the schemas must be contained in the subset of XSD that DCS supports, which you can find here:
http://msdn.microsoft.com/en-us/library/ms733112.aspx
If svcutil is failing to generate a proxy when you specify “/serializer:DataContractSerializer”, then the most likely explanation is that the schema isn’t DC-conformant. Do you see any other errors or warnings when you use svcutil?
It’s also generally bad practice to use DataSets (both typed and untyped) and IXmlSerializables in public web services. In this case, it seems there might be difficulties in importing these. Here’s a quick link for some other reasons it can be problematic: http://www.hanselman.com/blog/PermaLink,guid,d88f7539-10d8-4697-8c6e-1badb08bb3f5.aspx
While DataContractSerializer can serialize IXmlSerializable types, there is no guarantee at all made that IXmlSerializable types can be imported as data contracts. Those are two different concepts. IXmlSerializable types are free to provide their own schemas, so it's possible for them to provide schemas that are not datacontract-compliant and thus cause svcutil to fall back to XmlSerializer type generation.
Hope this helps.

Inherit from a class in a wcf service

I want to inherit from a class which is located in a WCF Service. The Inheritance works fine (I see the properties of the base class in my child class), my problem occurs when I try to call a method from the service and pass the childtype as a parameter and not the basetype.
Base class in WCF Service (Pet):
[ServiceContract]
public interface IService
{
[OperationContract]
void BringPet(Pet pet);
[OperationContract]
void TakePet(Pet pet);
[OperationContract]
List<Pet> GetAllPets();
}
[DataContract]
public class Pet
{
private string _name;
private string _color;
[DataMember]
public string Name
{
get { return _name; }
set { _name = value; }
}
[DataMember]
public string Color
{
get { return _color; }
set { _color = value; }
}
}
Class on the client (Dog inherits from Pet):
[DataContract()]
class Dog : PetService.Pet
{
[DataMember()]
public bool HasPedigree { get; set; }
[DataMember()]
public string Race { get; set; }
}
When I try calling something like this:
Dog dog = new Dog()
{
Color = "Black",
Name = "Pluto",
HasPedigree = true,
Race = "Travolta"
};
_client.BringPet(dog);
I get a CommunicationException which says that the type Dog is not expected by the method BringPet(Pet pet).
I would solve this problem by setting the KnownType attributes on the service side, but because my service must not know the type Dog I can't set the KnownType or ServiceKnownType attributes.
Can someone help me out?
If you want to have inherited classes that get returned instead of the ones defined in your service contract, you need to make this fact known to WCF by means of the ServiceKnownType attribute:
[ServiceContract]
[ServiceKnownType(typeof(Dog))]
public interface IService
{
[OperationContract]
void BringPet(Pet pet);
[OperationContract]
void TakePet(Pet pet);
[OperationContract]
List<Pet> GetAllPets();
}
This basically tells the WCF service to also allow classes of type Dog to be used in your service.
Or you can decorate your data contract with the KnownType attribute:
[DataContract]
[KnownType(typeof(Dog))]
public class Pet
{
.....
}
to associate your data contract type with additional types that could be used in its place.
One way or the other: you have to make your extra type known to the WCF runtime - WCF cannot figure out that fact by just checking .NET type inheritance.
I think this is very similar to this question: Service - client interface, architecture advice
As you will see, there is no really easy way to do this without making your service operation more generic.

Categories

Resources