Можно ли добавить DataMember в CollectionDataContract в WCF?

У меня есть класс коллекции, который я украсил CollectionDataContract. Класс collection также имеет свойство класса, которое я хотел бы передать клиенту сервиса. Я попытался добавить [DataMember] к этому свойству, но не добавил его в класс в клиенте, когда обновляюсь.

Любые эксперты WCF могут помочь?

Ответ 1

Рабочее решение опубликовано в моем блоге:

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

UPD: Спасибо, за ваш комментарий, Джефф. Здесь приведено краткое описание не общего класса. Полное общее решение можно найти в новом посте в моем блоге: 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

Возможно, вам стоит рассмотреть возможность внедрения пользовательской сериализации для вашего класса. Может быть проще всего реализовать IXmlSerializable, вывести значения вашего настраиваемого свойства, а затем использовать DataContractSerializer, чтобы сериализовать экземпляры дочерних элементов в коллекции на выходе.

Ответ 3

Этот ответ предназначен как дополнение к ответу @BorisModylevsky. Каждое решение, которое я мог бы найти по этой проблеме, предлагалось с помощью IXmlSerializable. Но вот моя проблема: у меня есть иерархическая структура parent-child, подобная этой

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

Чтобы сделать эту работу для DataContractSerializer, мне пришлось добавить некоторые частные конструкторы и необходимые атрибуты:

[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() { }
}

Проблема в том, что XML создается для коллекции, выглядящей так:

<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>

В XML отсутствует свойство Parent в Items. Лучше всего сказать, CollectionDataContract не поддерживает никаких дополнительных свойств в классе коллекции.

В этом случае, если бы я реализовал IXmlSerializable, как это было предложено многими, тогда я собирался зациклить вручную сериализацию и десериализацию огромного количества структур внутри ItemDetails (что я не показал здесь для краткости). В этом случае DataContractSerializer будет почти бесполезным для меня.

Но с ответом @BorisModylevsky выше, я изменил код до этого:

[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() { }
}

Подводя итог: я изменил CollectionDataContract на DataContract и добавил DataMember в закрытый список, который я использую в коллекции. Это работает, так как DataContractSerializer может получать и устанавливать частные свойства/поля. И теперь XML выглядит так:

<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>

Что для моих целей отлично работает. Он сериализуется и десериализуется по мере необходимости.