开发者

WCF and interfaces

I need to have 2 families of classes (one on server and one on client side) which are identical in data structure but differs in behavior. Also I suppose that these fmailies will be enough big, thus I don't want to implement intermediate level of DTO and transformations into and from it. I decided to move in following manner: declare shared assembly with declaration of data and services interfaces like these ones:

public interface ITest
{
    string Title { get; set; }
    int Value { get; set; }
}
public interface IService
{
    ITest GetData();
}

Having these declarations I can implement these interfaces on server side for example basing on Entity Framework (data) and WCF (services). On the client side I can use for example Dependency Properties (data) and WCF (service). When I started trying to implement this, I met several troubes. First one was about server side of WCF - it simply do not want to work with interfaces as return parameters. Thanks to StackOverflow this issue was resolved like here .

Next problem is that XML rendered by server side includes qulified assembly name of serialized on the server class.

      <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">  
        <s:Body>
          <GetDataResponse xmlns="http://tempuri.org/">
            <Test z:Id="1" z:Type="Dist.Server.Model.Test" z:Assembly="Dist.Server, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" xmlns="http://schemas.datacontract.org/2004/07/Dist.Server.Model" xmlns:i=开发者_如何学Go"http://www.w3.org/2001/XMLSchema-instance" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
              <Title z:Id="2">Test</Title>
              <Value>123</Value>
            </Test>
          </GetDataResponse>
        </s:Body>
      </s:Envelope>

Thus during deserialization on client side there was an attempt to load this type. As this type is inaccessible on client side, I had to implement some kind of type mapping. I found that this is quite easy as NetDataContractSerializer used for serialization supports Binder property. Thus I override this property on client side and return correct value (hardcode in meantime, but it's OK for tests).

public class NetBinder : SerializationBinder
{
    public override Type BindToType(string assemblyName, string typeName) {
        var type = Type.GetType("Client.Test, Client, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
        return type;
    }
}

Now I have following picture:

- Server uses NetDataContractSerializer to serialize response. It uses actual value (calss) during serialization instead of type used in declaration of service (interface).

- Client side receives XML and starts deserialization. To resolve type, NetDataContractSerializer calls my Binder that returns correct type.

- NetDataContractSerializer creates instance of correct type and starts loading of its properties.

And here I got a trouble that I don't know how to resolve. Values of properties are not deserialized. It means that instance of class is created correctly (uninitialized instance created through reflection services), but all properties are in their default values (0 or null). I tried to play with declaration of class on client side: mark it as [Serializable], implement ISerializable, etc., but nohing is helpful. NetDataContractSerializer requires class to be marked as [DataContract] or [Serializable]. First option leaves properties empty, second one causes exceptions like "</Test> is unexpected, expected is bla-bla-bla_Value_Ending_bla-bla-bla".

Does anybody have any suggestions on how to resolve this last step?

I can provide full sources for better understanding, but I don't know ifI can attach them here...

Thanks in advance.


You could have a look at frameworks like AutoMapper that would take care the transformation to and from DTO. This would make your life much easier.

Instead of using an interface why not create a base class containing only the data and inherit it on both sides by sharing the assembly containing this class. Playing around with the ServiceKnownType should help you fix the last issues.

You may also share the same base classes on both sides and implement the specific logic as extension methods.


Seems that problem was solved enough easily. I created own serializer and used it instead of NetDataContractSerializer. Code is quite simple:

public class MySerializer: XmlObjectSerializer
{
    public override void WriteStartObject(XmlDictionaryWriter writer, object graph) {
    }

    public override void WriteObjectContent(XmlDictionaryWriter writer, object graph) {
        var formatter = new XmlSerializer(graph.GetType());
        formatter.Serialize(writer, graph);
    }

    public override void WriteEndObject(XmlDictionaryWriter writer) {
    }

    public override object ReadObject(XmlDictionaryReader reader, bool verifyObjectName) {
        var realType = Type.GetType("Client.Test, Client, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"); //temporary solution
        var formatter = new XmlSerializer(realType);
        return formatter.Deserialize(reader);
    }

    public override bool IsStartObject(XmlDictionaryReader reader) {
        return true;//temporary solution
    }

}

I checked SOAP that goes from server to client and it's almost the same as NetDataSerializer renders. The only difference is in attribute xmlns="".

Kai, Johann thanksfor your tries.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜