WCF - несколько сервисных контрактов, использующих одни и те же контракты данных

У меня новый вопрос к гуру WCF.

Итак, у меня есть класс User, который близок к представлению «Пользователь» из БД, которое я использую для операций с базой данных. Теперь я хотел бы иметь 2 разных сервисных контракта, которые используют этот класс в качестве контракта данных, но каждый по-своему ... Я имею в виду,

public class DBLayer
{
    void InsertUsers(List<User> userList)
    {
        // both 'PropertyVisibleForService1' and 'PropertyVisibleForService2'
        // are used HERE to be inserted into their columns 
    }
}

[DataContract]
public class User
{
  [DataMember] public string PropertyVisibleOnlyForService1{...}
  [DataMember] public string PropertyVisibleOnlyForService2{...}
}

[ServiceContract]
public interface IService1  
{   
   List<User> GetUsers();  // user with 'PropertyVisibleOnlyForService1' inside
}

[ServiceContract]
public interface IService2  
{   
    List<User> GetUsers(); // user with 'PropertyVisibleOnlyForService2' inside 
}

Итак, идея состоит в том, что у каждой службы будет свой тип пользователя, подмножество 'User'. Имея в виду, что я хочу использовать 'User' как есть для операций с БД, каковы были бы мои варианты достижения этого? Действительно ли мне нужно создавать разные контракты данных или есть другой способ поумнее?

Лучше всего не только дать мне решение, но и объяснить некоторые передовые практики и альтернативы.

Заранее спасибо.

EDIT1: я добавил здесь фиктивный класс DBLayer для лучшего обзора и почему я думаю, что наследование может быть не лучшим в этом случае.

Решением было бы иметь еще «UserForService1» и «UserForService2» в качестве контрактов данных, которые в конце отображали бы / в «User», но мне нужны были другие точки зрения.

EDIT2: Очень хорошая статья, которая помогла мне в этом случае: http://bloggingabout.net/blogs/vagif/archive/2009/03/29/iextensibledataobject-is-not-only-for-backward-compatibility.aspx


person Learner    schedule 06.05.2011    source источник
comment
Я не эксперт по WCF, поэтому предлагаю в комментарии: Похоже, у вас должно быть два класса, производных от User; один имеет свойство PropertyVisibleOblyForService1, а другой - другое свойство.   -  person phoog    schedule 06.05.2011
comment
Спасибо за ответ ... Итак, вы порекомендовали удалить эти свойства и поместить в новые производные классы. Дело в том, что я бы хотел, чтобы класс «Пользователь» не менялся.   -  person Learner    schedule 06.05.2011
comment
Да, думаю, вы понимаете мою рекомендацию. Но поскольку я не эксперт по WCF и не знаю ваш проект, требования и т. Д., Я не знаю, можно ли делать то, что вы хотите, не изменяя класс User. Если вы опубликуете более подробную информацию, я, возможно, смогу помочь, или, может быть, кто-то с большим опытом WCF внесет свой вклад. Удачи в любом случае!   -  person phoog    schedule 06.05.2011
comment
Спасибо за участие, любые комментарии приветствуются. Я знаю, что одним из решений было бы создать 2 разных контракта данных, а затем сопоставить «Пользователя» с соответствующим контрактом данных, но, возможно, это еще одна лучшая практика для этого случая. Не думаю, что в моем случае наследование подойдет. Спасибо.   -  person Learner    schedule 06.05.2011
comment
Итак, все предлагали одно и то же. Может быть, я недостаточно ясно выразился в своей просьбе. Я не хочу извлекать свойства из «User» и помещать их в какие-то другие производные классы, поскольку этот «User» (и его свойства используются для операций с БД КАК ЕСТЬ). Таким образом, «PropertyVisibleOnlyForService1» и «PropertyVisibleOnlyForService2» являются столбцами в пользовательской таблице.   -  person Learner    schedule 09.05.2011
comment
... или только если я могу скрыть эти свойства в производных классах?   -  person Learner    schedule 09.05.2011


Ответы (4)


Вы можете создать отдельные DTO для каждой службы, но ваш случай на самом деле будет идеальным для шаблона декоратора:

[DataContract]
public class UserForService1 : User
{
     private User mUser;
     public UserForService1(User u)
     {
         mUser = u;
     }

     //expose only properties you'd like the user of this data contract to see
     [DataMember]
     public string SomeProperty
     {
         get
         {
            //always call into the 'wrapped' object
            return mUser.SomeProperty;
         }
         set
         {
            mUser.SomeProperty = value;
         }
     }
     // etc...
}

и для Service2 аналогичный код, который раскрывает только то, что вам нужно ...

person veljkoz    schedule 09.05.2011
comment
Это был ответ, который я искал. Очень хорошо! Ваше здоровье! - person Learner; 09.05.2011

Если они предназначены для представления разных типов пользователей, они должны относиться к разным классам. Я согласен с phoog в комментариях, вы должны унаследовать нужный тип от общего класса User и добавить определенные свойства службы в производные классы.

Почему вы не думаете, что наследование в этом случае было бы хорошо? Если вы предоставите нам более подробную информацию, мы можем попытаться изменить предложения в соответствии с вашей реальной проблемой.

person Timothy Strimple    schedule 06.05.2011

Как предлагается в комментарии, у вас может быть два класса, производных от базового пользователя, а затем с использованием данных Известные типы контрактов, вы можете достичь желаемой цели. См. Следующие ссылки для получения дополнительных примеров.

http://www.freddes.se/2010/05/19/wcf-knghtype-attribute-example/

http://footheory.com/blogs/bennie/archive/2007/07/28/handling-data-contract-object-hierarchies-in-wcf.aspx

person Garett    schedule 06.05.2011
comment
Учитывая тот факт, что PropertyVisibleOnlyForService1 и PropertyVisibleOnlyForService2 используются пользователем для операций с базами данных, поскольку являются столбцами в таблице db ... это означает, что я не могу извлечь эти свойства из пользователя, чтобы поместить их в производные классы, если только Я их как-то прячу? Может, стоит попробовать. - person Learner; 09.05.2011

Если вы не хотите использовать наследование, например:

[DataContract]
public class User
{
}

[DataContract]
public class Service1User : User
{
  [DataMember] public string PropertyVisibleOnlyForService1{...}
}

[DataContract]
public class Service2User : User
{
  [DataMember] public string PropertyVisibleOnlyForService2{...}
}

[ServiceContract]
public interface IService1  
{   
   List<Service1User> GetUsers();  // user with 'PropertyVisibleOnlyForService1' inside
}

[ServiceContract]
public interface IService2  
{   
    List<Service2User> GetUsers(); // user with 'PropertyVisibleOnlyForService2' inside 
}

Тогда я не уверен, что ты будешь делать. Ваш вид нарушения принципов объявления типа в этот момент. Подумайте об этом обычным .NET-способом; если вы определите «Пользователь» в своем приложении, то он везде будет одного и того же типа. Некоторые свойства нельзя скрыть от других классов или методов.

WCF также собирается упаковать информацию об этом типе в сгенерированный WSDL, и он будет определять тип User только один раз, поэтому ему необходимо знать, какие свойства существуют.

Теперь, если все, что вас волнует, это фактическое созданное сообщение SOAP, и вам не важен WSDL или то, что увидят любые клиенты, созданные с помощью WSDL, тогда технически вы можете не передавать это свойство в сообщение SOAP. когда он равен нулю, выполнив:

    [DataMember(EmitDefaultValue=false)]

Затем, когда это свойство равно нулю, оно не будет включено в сериализацию. Однако это не имело бы никакого значения, если бы клиент был сгенерирован из WSDL, поскольку его тип User все равно должен был бы содержать оба свойства. Это просто изменит сериализацию, чтобы вместо отправки клиенту что-то вроде:

<User>
  <PropertyVisibleOnlyForService1 nil="true" />
  <PropertyVisibleOnlyForService2>something</PropertyVisibleOnlyForService2>
</User>

вместо этого он отправит:

<User>
  <PropertyVisibleOnlyForService2>something</PropertyVisibleOnlyForService2>
</User>
person CodingWithSpike    schedule 06.05.2011
comment
Спасибо за вашу точку зрения и за то, как пользоваться функцией Emit. Я думаю, хорошо иметь это в виду, возможно, я смогу использовать его в будущем. - person Learner; 09.05.2011