WCF - несколько сервисных контрактов, использующих одинаковые контракты данных - PullRequest
7 голосов
/ 06 мая 2011

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

Итак, у меня есть класс User, который близок к представлению '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

Ответы [ 4 ]

4 голосов
/ 09 мая 2011

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

[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, который раскрывает только то, что вам нужноибо там ...

1 голос
/ 06 мая 2011

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

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

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

1 голос
/ 06 мая 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, и он собирается определить тип пользователя только один раз, поэтому ему нужно знать, какие свойства там есть.

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

    [DataMember(EmitDefaultValue=false)]

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

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

вместо этого будет отправлено:

<User>
  <PropertyVisibleOnlyForService2>something</PropertyVisibleOnlyForService2>
</User>
1 голос
/ 06 мая 2011

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

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

...