2016-09-09 2 views
1

Этот вопрос появляется снова и снова. Это не было проблемой для .NET и Silverlight, но с другой стороны, с тех пор я никогда не видел способа указывать известные типы без физического ввода их в свой ServiceContract. Это означает, что этот список нельзя динамически изменять во время выполнения. Это проблема в Xamarin, UWP и, возможно, в других платформах. Итак, давайте посмотрим на это.WCF - указать известные типы (стандарт PCL/.NET)

Первоначально одно решение этой проблемы на .NET и Silverlight был указать способ получения известных типов на ServiceKnownType так:

[ServiceKnownType("GetKnownTypes", typeof(GetTypesHelper))] 

Это всегда хорошо работала на .NET и Silverlight, но он не работает на UWP или Xamarin. Я пробовал это сегодня, и это ошибка, которую я получаю:

System.InvalidOperationException: ServiceKnownTypeAttribute указывает метод GetKnownTypes в типе Adapt.XivicClient.WCF.ServiceContracts.GetTypesHelper, которого не существует. Метод должен быть статическим и принимает один параметр типа ICustomAttributeProvider

Конечно, библиотеки PCL и .NET Standard не имеют класса ICustomAttributeProvider, поэтому этого не может быть сделано. Итак, я пробовал это другое возможное решение: https://stackoverflow.com/a/2104482/1878141

Это работает, указав поведение службы. Но, опять же, PCL и Standard не имеют класса IServiceBehavior и не говорят Android.

Я пробовал этот код, потому что я думал, что могу заменить DataContractSerializer, но я получаю NotImplementedException на Android.

 dataAccessServiceClient.Endpoint.EndpointBehaviors.Add(new XivicServicBehaviour()); 

public class XivicServicBehaviour : IEndpointBehavior 
{ 
    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) 
    { 
    } 

    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) 
    { 
    } 

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) 
    { 
    } 

    public void Validate(ServiceEndpoint endpoint) 
    { 
    } 
} 

Итак, какие у нас варианты?

+0

возможность перехода на дизайн REST API всегда есть. Даже для WCF сами известные типы менее рекомендуются. –

+0

Да. Я просто говорил об этом в моей команде. Вероятно, я смогу портировать наш код в REST в конечном итоге, но есть много вызовов, и кажется смешным идти на все эти проблемы, когда WCF работает отлично. Я не знаю, что вы имеете в виду, когда говорите, что известные типы менее рекомендуются. Известные типы лежат в основе сериализации WCF. –

ответ

0

У меня есть ответ на мой вопрос. Он долго смотрел мне в лицо и очень прост.

Я не знаю почему, но нам обычно рекомендуется использовать аргументы OperationContract для указания известных типов. Что-то вроде 8 лет, я думал, что это единственный способ сделать это. Я всегда знал, что вы можете указывать известные типы при ручном сериализации/десериализации с помощью DataContractSerializer, но я не знал, как переопределить функциональные возможности WCF по умолчанию для создания экземпляра DataContractSerializer, который я укажу.

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

Это для стандартного прокси-сервера .NET (Task Based Операции)

public class ServiceClientBase<T> : ClientBase<T>, IServiceClient, ICommunicationObject where T : class 
{ 
    #region Constructor 
    public ServiceClientBase(Binding binding, EndpointAddress endpointAddress, sc.IKnownTypeGetter knownTypeGetter) : base(binding, endpointAddress) 
    { 
     foreach (var operation in Endpoint.Contract.Operations) 
     { 
      var knownTypes = knownTypeGetter.GetKnownTypes(); 

      foreach (var type in knownTypes) 
      { 
       operation.KnownTypes.Add(type); 
      } 
     } 
    } 
    #endregion 

    #region Open/Close 
    public virtual Task OpenAsync() 
    { 
     var communicationObject = this as ICommunicationObject; 
     return Task.Factory.FromAsync(communicationObject.BeginOpen(null, null), new Action<IAsyncResult>(communicationObject.EndOpen)); 
    } 

    public virtual Task CloseAsync() 
    { 
     var communicationObject = this as ICommunicationObject; 
     return Task.Factory.FromAsync(communicationObject.BeginClose(null, null), new Action<IAsyncResult>(communicationObject.EndClose)); 
    } 
    #endregion 
} 

Вот еще один вкус:

public partial class WCFClientBase<T> : ClientBase<T> where T : class 
{ 
    public WCFClientBase(Binding binding, EndpointAddress endpointAddress, IKnownTypeGetter knownTypeGetter) : base(binding, endpointAddress) 
    { 
     foreach (var operation in Endpoint.Contract.Operations) 
     { 
      var knownTypes = knownTypeGetter.GetKnownTypes(); 

      foreach (var type in knownTypes) 
      { 
       operation.KnownTypes.Add(type); 
      } 
     } 
    } 
} 
Смежные вопросы