2011-08-03 11 views
5

primer lugar el código:¿Cuál es la mejor manera de serializar una matriz basada en una interfaz en WCF?

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

Un despachador codifica los datos como XML y lo envía a una instancia IWorker donde se evalúa cada expresión. Cuando una instancia de IWorker evalúa una expresión como verdadera, se invoca IAction.Execute y se pasa el xml/data.

¿Cuál es la mejor manera de serializar Rule.Actions? Empecé a escribir un serializador personalizado, pero preferiría ver si hay una manera más fácil.

Gracias.

+1

propiedades deben ser decorados con DataMember attr. OperationContract se usa para los métodos del servicio. –

Respuesta

4

No creo que pueda usar interfaces en DataContracts (alguien me corrige si estoy equivocado, pero supongo que eso es como tratar de usar un genérico también). Lo que hago, es tener una clase padre, luego agregar el atributo KnownType. Por ejemplo

[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;} 

} 

Ahora usted puede rellenar cualquier objeto que hereda del objeto de acción de los padres en la lista de acciones, y serializará adecuadamente todas sus propiedades de clase respectivas (siempre y cuando el objeto aparece como una knowntype) .

* He usado el nombre "acción" como un ejemplo de relacionarse con la suya, obviamente, la acción es una palabra clave en .NET

+0

+1 para el uso del atributo '[KnownType]', pero estoy bastante seguro de que puede usar interfaces aquí. Aún puede devolver una 'Lista ' siempre que las clases que implementan 'IAction' estén incluidas en los atributos' KnownType', y que también sean '[DataContract]' s. De hecho, podría estar pensando en utilizar interfaces como tipos de devolución desde los contratos de operación eh, que sé que puedes hacer. Quizás no puedas usar interfaces de contratos de datos ... hmmm, no tengo VS abierto para intentarlo ... – CodingWithSpike

+0

Empecé con KnownType. Me preguntaba si había una manera diferente. Parece tonto forzar una dependencia de atributo para resolver un tipo heredado. – Steve

0

serialización es el proceso de convertir entre un objeto datos y bytes que pueden ser transferidos a través el alambre. Las interfaces definen comportamiento, por lo que WCF por defecto no puede serializar tales datos. Sin embargo, si tiene los mismos ensamblajes exactos en el cliente y el servidor, puede usar el NetDataContractSerializer, que esencialmente serializará (y podrá serializar) toda la información del tipo de los objetos que se serializan, para que pueda recrearse en el otro lado.

El código siguiente muestra cómo utilizar el NetDataContractSerializer en un servicio para que (basado en el ejemplo principal de esto, el puesto de Aaron Skonnard en 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

Las interfaces definen más que el comportamiento, también pueden tener propiedades :-) –

+1

Las propiedades son esencialmente azúcar sintáctico sobre un par de métodos get/set. Y la interfaz define que la clase que los implementa también debe implementar esos "métodos". – carlosfigueira

Cuestiones relacionadas