开发者

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 enc开发者_开发技巧odes 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();
    }
}
0

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜