WCF: предоставление свойств DataMember только для чтения без набора?

у меня есть класс на стороне сервера, который я делаю доступным на стороне клиента через [DataContract]. Этот класс имеет поле readonly, которое я хотел бы сделать доступным через свойство. Однако я не могу этого сделать, потому что не похоже, что мне разрешено добавлять свойство [DataMember] без наличия get и set.

Итак - есть ли способ иметь свойство [DataMember] без сеттера?

[DataContract]
class SomeClass
{
    private readonly int _id; 

    public SomeClass() { .. }

    [DataMember]
    public int Id { get { return _id; } }        

    [DataMember]
    public string SomeString { get; set; }
}

или решение будет использовать [DataMember] в качестве поля - (например, показано здесь)? Пробовал делать это тоже, но, похоже, ему все равно, что поле только для чтения..?

редактировать: единственный способ сделать свойство readonly, взломав его так? (нет - я не хочу этого делать...)

[DataMember]
public int Id
{
    get { return _id; }
    private set { /* NOOP */ }
}

5 ответов


ваш класс " на стороне сервера "не будет" доступен " клиенту, на самом деле.

происходит следующее: на основе контракта данных клиент создаст новый отдельный класс из XML-схемы службы. Это не может используйте серверный класс как таковой!

он повторно создаст новый класс из определения схемы XML, но эта схема не содержит каких-либо специфических вещей .NET, таких как видимость или модификаторы доступа - это просто схема XML, в конце концов. Класс на стороне клиента будет создан таким образом, чтобы он имел тот же "след" на проводе - например, он сериализуется в тот же формат XML, в основном.

вы не может "транспорт" .NET конкретные ноу-хау о классе через стандартную службу на основе SOAP - в конце концов, все, что вы проходите вокруг сериализованного сообщения - никаких классов!

установите флажок "четыре принципа SOA" (определяется Don Box of Microsoft):

  1. границы четкие
  2. услуги автономной
  3. службы разделяют схему и контракт, а не класс
  4. совместимость основана на политике

см. пункт #3-Схема и контракт совместного использования служб,не class - вы только когда - либо разделяете интерфейс и XML - схему для контракта данных-это все-нет классов .NET.


поместите атрибут DataMember в поле, не являющееся свойством.

помните мысль, что WCF не знает инкапсуляции. Инкапсуляция-это термин ООП, а не SOA.

тем не менее, помните, что поле будет читаться только для людей, использующих ваш класс - любой, кто использует сервис, будет иметь полный доступ к полю на своей стороне.


у меня были некоторые свойства в классе в моем слое обслуживания, который я хотел передать Silverlight. Я не хотел создавать новый класс.

не "рекомендуем", но это казалось меньшим из двух зол, чтобы пройти через Total свойство silverlight (исключительно для визуальной привязки данных).

public class PricingSummary
{
    public int TotalItemCount { get; set; } // doesnt ideally belong here but used by top bar when out of store area

    public decimal SubTotal { get; set; }
    public decimal? Taxes { get; set; }
    public decimal Discount { get; set; }
    public decimal? ShippingTotal { get; set; }
    public decimal Total
    {
        get
        {
            return + SubTotal
                   + (ShippingTotal ?? 0)
                   + (Taxes ?? 0)
                   - Discount;
        }
        set
        {
            throw new ApplicationException("Cannot be set");
        }
    }
}

есть способ достичь этого. Но имейте в виду, что это напрямую нарушает следующий принцип, приведенный в ответ:

"3. Службы совместно используют схему и контракт, а не класс."

если это нарушение не касается, вот что:

  1. переместите контракты на обслуживание и данные в отдельную (портативную) библиотеку классов. (Назовем это собраниеSomeService.Contracts.) Вот как бы вы определили неизменный [DataContract] класс:

    namespace SomeService.Contracts
    {
        [DataContract]
        public sealed class Foo
        {
            public Foo(int x)
            {
                this.x = x;
            }
    
            public int X
            {
                get
                {
                    return x;
                }
            }
    
            [DataMember]  // NB: applied to the backing field, not to the property!
            private readonly int x;
        }
    }
    

    отметим, что [DataMember] применяется к вспомогательному полю, и не к соответствующему свойству только для чтения.

  2. ссылка на сборку контракта из вашего проекта приложения-службы (я буду называть мой SomeService.Web) и из ваших клиентских проектов (Мой называется SomeService.Client). Это может привести к следующим зависимостям проекта внутри вашего решение:

    screenshot highlighting the project dependencies in Solution Explorer

  3. далее при добавлении ссылки на службу в клиентский проект убедитесь, что включена опция "типы повторного использования", и убедитесь, что сборка контракта (SomeService.Contracts) будет включен в этот:

    screenshot highlighting the relevant service reference setting

вуаля! Visual Studio вместо создания нового Foo введите из схемы WSDL службы, будет повторно использовать неизменяемый Foo введите из вашего контракта собрание.

последнее предупреждение: вы уже отклонились от принципов обслуживания, приведенных в этот другой ответ. Но постарайся больше не сбиваться с пути. У вас может возникнуть соблазн начать добавлять (бизнес) логику в классы контрактов данных; не делайте этого. Они должны оставаться как можно ближе к немым объектам передачи данных (DTOs), как вы можете управлять.


определите контракт на обслуживание (интерфейс) перед реализацией контракта с использованием класса.