2009-01-23 7 views
13

Tengo una clase de colección que he decorado con un CollectionDataContract. La clase de colección también tiene una propiedad en la clase que me gustaría pasar al cliente del servicio. Intenté agregar [DataMember] a esa propiedad, pero no lo agregué a la clase en el cliente cuando lo actualicé.¿Puedo agregar un DataMember a un CollectionDataContract en WCF?

¿Algún experto de WCF tiene alguna ayuda que ofrecer?

Respuesta

12

una solución de trabajo está publicado en mi blog:

http://borismod.blogspot.com/2009/04/wcf-collectiondatacontract-and.html

UPD: Gracias, por tu comentario, Jeff. Aquí hay un resumen aquí de una clase no genérica. Una solución genérica completa se puede encontrar en una nueva entrada en mi blog: http://borismod.blogspot.com/2009/06/v2-wcf-collectiondatacontract-and.html


[DataContract(IsReference = true)]  
public class EntityCollectionWorkaround : ICollection 
    { 

     #region Constructor 
     public EntityCollectionWorkaround() 
     { 
      Entities = new List(); 
     } 
     #endregion 

     [DataMember] 
     public int AdditionalProperty { get; set; } 

     [DataMember] 
     public List Entities { get; set; } 

     #region ICollection Members 
      // Implement here members of ICollection by wrapping Entities methods 
     #endregion 

     #region IEnumerable Members 
      // Implement here members of IIEnumerable by wrapping Entities methods 
     #endregion 

    } 
 
+2

Buena publicación en el blog, pero sería bueno que dieras un breve resumen aquí. –

+0

¿Esto no expone un elemento '' en el XML? La solución preferible sería tener 'AdditionalProperty' como un atributo XML de' EntityCollectionWorkaround' y luego tener cada elemento '' como hijo directo de 'EntityCollectionWorkaround'. Pero supongo que esto es imposible con la serialización de DataContract? –

+0

Sí, lo hace, pero no veo qué pasa con esto? Supongo que su sugerencia implica que se utiliza algún tipo de serialización XML. Prefiero separar el método de serialización (binario, xml o lo que sea) y la definición de clase. Aunque adoptemos su sugerencia, AdditionalProperty estará expuesto en XML. –

3

Es posible que desee considerar la implementación de serialización personalizada para su clase. Puede ser más fácil implementar IXmlSerializable, generar los valores de su propiedad personalizada y luego usar el DataContractSerializer para serializar las instancias de los elementos secundarios en la colección al resultado.

+0

Para mi caso, 'IXmlSerializable' funcionaría, pero las estructuras dentro de la colección eran grandes, y una vez que implementé' ReadXml' y 'WriteXml' en la colección, iba a tener que escribir manualmente todo debajo de eso en el jerarquía...en ese punto, yo también podría personalizar rodar todo. Agregué una respuesta a continuación describiendo cómo obtuve 'IXmlSerializable' utilizando el atributo' DataContract' normal en la colección. – jwatts1980

1

Esta respuesta está pensado como un complemento a la respuesta @BorisModylevsky dio. Cada solución que pude encontrar para este problema sugirió usar IXmlSerializable. Pero aquí es mi problema: Tengo una estructura padre-hijo jerárquica como esto

public class Project 
{ 
    //Several properties here... 

    public ItemCollection Items { get; private set; } 

    public Project() 
    { 
     Items = new ItemCollection(); 
    } 
} 

public class ItemCollection : IList<ItemDetails> 
{ 
    private List<ItemDetails> _list = new List<ItemDetails>(); 

    public Project Parent { get; private set; } 

    ///dependency injection... 
    public ItemCollection(Project parent) 
    { 
     this.Parent = parent; 
    } 

    //Interface methods here working with _list... 
    //In the `Add(ItemDetails item)` method, I update incoming items with item.Parent = this.Parent. 
} 

public class ItemDetails 
{ 
    public Project Parent { get; set; } 

    //Several properties here, including some sub classes 

    ///dependency injection... 
    public ItemDetails(Project parent) 
    { 
     this.Parent = parent; 
    } 
} 

Con el fin de hacer este trabajo para DataContractSerializer he tenido que añadir algunos constructores privados, y los atributos necesarios:

[DataContract(IsReference=true)] 
public class Project 
{ 
    [DataMember(Order=0)] 
    public ItemCollection Items { get; private set; } 

    //[DataMember(Order=##)] 
    //Several properties here, including some other sub classes 

    public Project() 
    { 
     Items = new ItemCollection(); 
    } 
} 

[CollectionDataContract(IsReference=true)] 
public class ItemCollection : IList<ItemDetails> 
{ 
    private List<ItemDetails> _list = new List<ItemDetails>(); 

    [DataMember(Order=0)] 
    public Project Parent { get; private set; } 

    ///dependency injection... 
    public ItemCollection(Project parent) 
    { 
     this.Parent = parent; 
    } 

    //Private constructor for use with DataContractSerializer 
    private ItemCollection() { } 

    //Interface methods here working with _list... 
} 

[DataContract(IsReference=true)] 
public class ItemDetails 
{ 
    [DataMember(Order=0)] 
    public Project Parent { get; private set; } 

    //[DataMember(Order=##)] 
    //Several properties here, including some sub classes 

    ///dependency injection... 
    public ItemDetails(Project parent) 
    { 
     this.Parent = parent; 
    } 

    //Private constructor for use with DataContractSerializer 
    private ItemDetails() { } 
} 

el problema es que se crea el XML para la recogida era la siguiente:

<Project> 
    <!--OTHER PROJECT PROPERTIES HERE--> 
    <Items z:Id="i11"> 
     <ItemDetails z:Id="i12"> 
      <Parent z:Ref="i1"/> 
      <!--OTHER PROPERTIES HERE--> 
     </ItemDetails> 
     <ItemDetails z:Id="i16"> 
      <Parent z:Ref="i1"/> 
      <!--OTHER PROPERTIES HERE--> 
     </ItemDetails> 
    </Items> 
</Project> 

no hay Parent propiedad bajo Items en el XML. Lo mejor que puedo decir es que CollectionDataContract no admite ninguna propiedad adicional en la clase de recopilación.

En este caso, si implementé IXmlSerializable como muchos habían sugerido, entonces iba a estar atascado manualmente serializando y deserializando una gran cantidad de las estructuras dentro de ItemDetails (que no he mostrado aquí por brevedad). En cuyo caso, DataContractSerializer sería inútil para mí.

Pero con @BorisModylevsky respuesta anterior, he cambiado el código para esto:

[DataContract(IsReference=true)] 
public class Project 
{ 
    [DataMember(Order=0)] 
    public ItemCollection Items { get; private set; } 

    //[DataMember(Order=##)] 
    //Several properties here, including some other sub classes 

    public Project() 
    { 
     Items = new ItemCollection(); 
    } 
} 

[DataContract(IsReference=true)] 
public class ItemCollection : IList<ItemDetails> 
{ 
    //Refactored _list to ItemsList. I didn't have to; I could have used [DataMember(Name="ItemsList", Order=1)] 
    [DataMember(Order=1)] 
    private List<ItemDetails> ItemsList = new List<ItemDetails>(); 

    [DataMember(Order=0)] 
    public Project Parent { get; private set; } 

    ///dependency injection... 
    public ItemCollection(Project parent) 
    { 
     this.Parent = parent; 
    } 

    //Private constructor for use with DataContractSerializer 
    private ItemCollection() { } 

    //Interface methods here working with ItemsList... 
} 

[DataContract(IsReference=true)] 
public class ItemDetails 
{ 
    [DataMember(Order=0)] 
    public Project Parent { get; private set; } 

    //[DataMember(Order=##)] 
    //Several properties here, including some sub classes 

    ///dependency injection... 
    public ItemDetails(Project parent) 
    { 
     this.Parent = parent; 
    } 

    //Private constructor for use with DataContractSerializer 
    private ItemDetails() { } 
} 

En resumen: me cambiaron a CollectionDataContractDataContract y ha añadido a la lista DataMember privada que estoy utilizando en la colección. Esto funciona desde DataContractSerializer puede obtener y establecer propiedades privadas/campos. Y ahora el XML se ve así:

<Project> 
    <!--OTHER PROJECT PROPERTIES HERE--> 
    <Items z:Id="i11"> 
     <Parent z:Ref="i1"/> 
     <ItemsList> 
      <ItemDetails z:Id="i12"> 
       <Parent z:Ref="i1"/> 
       <!--OTHER PROPERTIES HERE--> 
      </ItemDetails> 
      <ItemDetails z:Id="i16"> 
       <Parent z:Ref="i1"/> 
       <!--OTHER PROPERTIES HERE--> 
      </ItemDetails> 
     </ItemsList> 
    </Items> 
</Project> 

Cuál para mis propósitos funciona perfectamente. Se serializa y deserializa según sea necesario.

Cuestiones relacionadas