WCF и интерфейсы - PullRequest
       1

WCF и интерфейсы

0 голосов
/ 28 марта 2011

Мне нужно иметь 2 семейства классов (одно на сервере и одно на стороне клиента), которые идентичны по структуре данных, но отличаются по поведению.Также я предполагаю, что эти почтовые сообщения будут достаточно большими, поэтому я не хочу реализовывать промежуточный уровень DTO и преобразований в него и из него.Я решил двигаться следующим образом: объявить совместно используемую сборку с объявлением интерфейсов данных и сервисов, подобных этим:

public interface ITest
{
    string Title { get; set; }
    int Value { get; set; }
}
public interface IService
{
    ITest GetData();
}

Имея эти объявления, я могу реализовать эти интерфейсы на стороне сервера, например, на основе Entity Framework (data) и WCF (услуги).На стороне клиента я могу использовать, например, Dependency Properties (данные) и WCF (сервис).Когда я начал пытаться реализовать это, я встретил несколько проблем.Сначала речь шла о серверной стороне WCF - он просто не хочет работать с интерфейсами в качестве возвращаемых параметров.Благодаря StackOverflow эта проблема была решена следующим образом: здесь .
Следующая проблема заключается в том, что XML, отображаемый на стороне сервера, содержит уточненное имя сборки, сериализованное в классе сервера.

      <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">  
        <s:Body>
          <GetDataResponse xmlns="http://tempuri.org/">
            <Test z:Id="1" z:Type="Dist.Server.Model.Test" z:Assembly="Dist.Server, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" xmlns="http://schemas.datacontract.org/2004/07/Dist.Server.Model" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
              <Title z:Id="2">Test</Title>
              <Value>123</Value>
            </Test>
          </GetDataResponse>
        </s:Body>
      </s:Envelope>

Таким образом, при десериализации на стороне клиента была попытка загрузить этот тип.Поскольку этот тип недоступен на стороне клиента, мне пришлось реализовать какое-то отображение типов.Я обнаружил, что это довольно просто, поскольку NetDataContractSerializer , используемый для сериализации, поддерживает свойство Binder .Таким образом, я переопределяю это свойство на стороне клиента и возвращаю правильное значение (в то же время жесткий код, но это нормально для тестов).

public class NetBinder : SerializationBinder
{
    public override Type BindToType(string assemblyName, string typeName) {
        var type = Type.GetType("Client.Test, Client, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
        return type;
    }
}

Теперь у меня есть следующая картина:
- Сервер использует NetDataContractSerializer для сериализации ответа.Он использует фактическое значение (calss) во время сериализации вместо типа, используемого в объявлении сервиса (интерфейса).
- Клиентская сторона получает XML и запускает десериализацию.Чтобы разрешить тип, NetDataContractSerializer вызывает мой Binder, который возвращает правильный тип.
- NetDataContractSerializer создает экземпляр правильного типа и начинает загрузку его свойств.
И вот я получилБеда, которую я не знаю, как решить.Значения свойств не десериализованы.Это означает, что экземпляр класса создан правильно (неинициализированный экземпляр создан с помощью служб отражения), но все свойства имеют значения по умолчанию (0 или ноль).Я попытался поиграть с объявлением класса на стороне клиента: пометить его как [Serializable], реализовать ISerializable и т. Д., Но ничто не помогает. NetDataContractSerializer требует, чтобы класс был помечен как [DataContract] или [Serializable].Первый параметр оставляет свойства пустыми, второй вызывает исключения, такие как « является неожиданным, ожидаемым является bla-bla-bla_Value_Ending_bla-bla-bla».

У кого-нибудь есть какие-либо предложения по решению этого последнего шага?

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

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

Ответы [ 2 ]

1 голос
/ 28 марта 2011

Вы можете взглянуть на такие фреймворки, как AutoMapper, которые позаботятся о преобразовании в и из DTO.Это сделало бы вашу жизнь намного проще.

Вместо использования интерфейса, почему бы не создать базовый класс, содержащий только данные, и наследовать его с обеих сторон, разделяя сборку, содержащую этот класс.Игра с ServiceKnownType должна помочь вам решить последние проблемы.

Вы также можете использовать одни и те же базовые классы с обеих сторон и реализовать определенную логику в качестве методов расширения.

0 голосов
/ 29 марта 2011

Кажется, что проблема была решена достаточно легко.Я создал собственный сериализатор и использовал его вместо NetDataContractSerializer .Код довольно прост:

public class MySerializer: XmlObjectSerializer
{
    public override void WriteStartObject(XmlDictionaryWriter writer, object graph) {
    }

    public override void WriteObjectContent(XmlDictionaryWriter writer, object graph) {
        var formatter = new XmlSerializer(graph.GetType());
        formatter.Serialize(writer, graph);
    }

    public override void WriteEndObject(XmlDictionaryWriter writer) {
    }

    public override object ReadObject(XmlDictionaryReader reader, bool verifyObjectName) {
        var realType = Type.GetType("Client.Test, Client, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"); //temporary solution
        var formatter = new XmlSerializer(realType);
        return formatter.Deserialize(reader);
    }

    public override bool IsStartObject(XmlDictionaryReader reader) {
        return true;//temporary solution
    }

}

Я проверил SOAP, который идет с сервера на клиент, и он почти такой же, как NetDataSerializer .Единственная разница в атрибуте xmlns = "".

Кай, Иоганн, спасибо за твои попытки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...