Использование интерфейсов с WCF - PullRequest
3 голосов
/ 16 ноября 2010

Я гуглял и читаю уже несколько часов, и я не могу найти никого, кто бы занимался моим конкретным сценарием ...

Я хочу использовать интерфейсы в моих контрактах на обслуживание WCF, чтобы свободно связать сервис склассы, используемые на каждом конце провода.Это позволит нам иметь сборку низкого уровня, которая содержит только контракты на обслуживание и данные (только интерфейсы), которые мы можем передать консультанту.На своем конце соединения они могут создавать экземпляры своих классов данных, которые реализуют наш интерфейс Data Contract, отправлять его по сети нам, и наша служба WCF затем преобразует / отдает / все эти входящие данные в нашу версию.класса данных, который реализует тот же интерфейс.

Вот пример.IDataContract содержит информацию, которую я хочу передать по проводам.Конечные точки и другие специфичные для WCF конфигурации - все по умолчанию (мои проблемы могут заключаться в этом, поэтому я могу включить больше, если мне нужно что-то изменить).

РЕДАКТИРОВАТЬ: я включил больше кода и переименовал пару классов, чтобы он был менее запутанным.Дополнения Name & Namespace к DataContractAttributes, а также два раздела в файлах конфигурации являются новыми дополнениями, основанными на информации из этого сообщения в блоге .Если я переключаюсь на абстрактный базовый класс вместо интерфейса, он работает .Тем не менее, я хотел бы, чтобы это работало с интерфейсом, если это возможно.

Общая библиотека (мой код, предоставленный авторам клиентов):

public interface IDataContract
{
    string MyProperty { get; set; }
}
[ServiceContract]
public interface ITestService
{
    [OperationContract]
    IDataContract TestSharedInterface(IDataContract clientData);
}

Код клиента (их):

[DataContract(Name = "IDataContract", Namespace = "http://services.sliderhouserules.com")]
public class ClientDataClass : IDataContract
{
    [DataMember]
    public string MyProperty { get; set; }
}
private static void CallTestSharedInterface()
{
    EndpointAddress address = new EndpointAddress("http://localhost/ServiceContractsTest.WcfService/TestService.svc");
    ChannelFactory<ITestService> factory = new ChannelFactory<ITestService>("ITestService", address);
    ITestService proxy = factory.CreateChannel();
    ((IClientChannel)proxy).Open();

    IDataContract clientData = new ClientDataClass() { MyProperty = "client data" };
    IDataContract serverData = proxy.TestSharedInterface(clientData);
    MessageBox.Show(serverData.MyProperty);
}

Конфигурация клиента:

<system.runtime.serialization>
    <dataContractSerializer>
        <declaredTypes>
            <add type="ServiceContractsTest.Contracts.DataContracts.IDataContract, ServiceContractsTest.Contracts">
                <knownType type="ServiceContractsTest.WcfClient.ClientDataClass, ServiceContractsTest.WcfClient"/>
            </add>
        </declaredTypes>
    </dataContractSerializer>
</system.runtime.serialization>

Код сервера (мой):

public class TestService : ITestService
{
    public IDataContract TestSharedInterface(IDataContract clientData)
    {
        ServerDataClass convertedClientData = (ServerDataClass)clientData;
        IDataContract serverData = new ServerDataClass() { MyProperty = convertedClientData.MyProperty + " + server data added" };
        return serverData;
    }
}
[DataContract(Name = "IDataContract", Namespace = "http://services.sliderhouserules.com")]
public class ServerDataClass : IDataContract
{
    [DataMember]
    public string MyProperty { get; set; }
}

Конфигурация сервера:

<system.runtime.serialization>
    <dataContractSerializer>
        <declaredTypes>
            <add type="ServiceContractsTest.Contracts.DataContracts.IDataContract, ServiceContractsTest.Contracts">
                <knownType type="ServiceContractsTest.WcfService.ServerDataClass, ServiceContractsTest.WcfService"/>
            </add>
        </declaredTypes>
    </dataContractSerializer>
</system.runtime.serialization>

Я получаю сообщение об ошибке сериализации при вызове клиента с жалобами на известные типы.Я просто пропускаю некоторую разметку метаданных в этом клиентском классе?Я в недоумении относительно того, где даже узнать, в чем заключается проблема, даже в том, что я перепробовал все поиски, о которых только мог подумать, и никто, похоже, не имел дела с этим конкретным сценарием.хотите ClientDataClass сериализовать в <IDataContract><MyProperty>client data</MyProperty></IDataContract> и затем иметь возможность десериализовать это в ServerDataClass экземпляр.Кажется, это должно быть возможно.

Ответы [ 3 ]

3 голосов
/ 16 ноября 2010

Если ваши контракты данных являются интерфейсами, WCF не может знать, какой объект создать для входящего запроса.Нет необходимости, чтобы класс был таким же, как в сервисе, после того, как Add Service Reference прочитает WSDL и сгенерирует новые классы на основе информации о типе в WSDL.

2 голосов
/ 31 июля 2011

Этот блог дает мне правильное направление, чтобы найти решение для моей проблемы. На самом деле у меня точно такой же сценарий, как sliderhouserules , описанный в его посте.

Но в моем сценарии я не могу использовать какой-либо абстрактный или базовый класс для наследования. Поэтому я использовал класс TypesHelper, чтобы самостоятельно прочитать раздел dataContractSerializer и передать соответствующие типы службе WCF.

namespace ExampleNamespace
{
  public interface IJustAInstance { }

  [ServiceContract]
  [ServiceKnownType("GetKnownTypes", typeof(ExampleNamespace.TypesHelper))]
  public interface ICreateInstance
  {
    IJustAInstance CreateInstance();
  }

  public static class TypesHelper
  {
    public static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider provider)
    {
      DataContractSerializerSection section = (DataContractSerializerSection)
        ConfigurationManager.GetSection(
        "system.runtime.serialization/dataContractSerializer");
      if (dataContractSerializerSection != null)
      {
        foreach (DeclaredTypeElement item in dataContractSerializerSection.DeclaredTypes)
        {
          foreach (TypeElement innterItem in item.KnownTypes)
          {
            Type type = Type.GetType(innterItem.Type);
            if (typeof(IJustAInstance).IsAssignableFrom(type ))
              yield return type;
          }
        }
      }
    }
  }
}
0 голосов
/ 16 ноября 2010

Вы можете создать BaseContract, который ваши ClientContract и ServerContract могут предоставить (как свойство) и который вы можете использовать в соответствующем конструкторе при создании новых экземпляров ClientContract или ServerContract.Тогда вам нужно только добавить BaseContract в вашу общую библиотеку.

...