【问题标题】:WCF - Specify Known Types (PCL / .NET Standard)WCF - 指定已知类型(PCL / .NET 标准)
【发布时间】:2016-09-09 01:37:34
【问题描述】:

这个问题一次又一次地出现。这在 .NET 和 Silverlight 上不是问题,但在其他所有平台上,我从未见过一种无需将已知类型物理输入到 ServiceContract 即可指定已知类型的方法。这意味着该列表不能在运行时动态更改。这是 Xamarin、UWP 和可能其他平台中的问题。那么,让我们看看这个。

最初,.NET 和 Silverlight 上解决此问题的一种方法是指定一种方法来获取 ServiceKnownType 上的已知类型,如下所示:

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

这在 .NET 和 Silverlight 上一直运行良好,但它不适用于 UWP 或 Xamarin。我今天试过了,这是我得到的错误:

System.InvalidOperationException:ServiceKnownTypeAttribute 指定类型 Adapt.XivicClient.WCF.ServiceContracts.GetTypesHelper 中不存在的方法 GetKnownTypes。该方法必须是静态的,并且需要一个 ICustomAttributeProvider 类型的参数

当然,PCL 和 .NET Standard 库没有 ICustomAttributeProvider 类,因此无法做到这一点。所以,我尝试了其他可能的解决方案: https://stackoverflow.com/a/2104482/1878141

这通过指定服务行为来工作。但是,PCL 和 Standard 没有 IServiceBehavior 类,Android 也没有。

我尝试了这段代码,因为我认为我可以替换 DataContractSerializer,但我在 Android 上得到了 NotImplementedException。

        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)
    {
    }
}

那么,我们有哪些选择?

【问题讨论】:

  • 始终存在迁移到 REST API 设计的选项。即使对于 WCF 本身,也不推荐使用已知类型。
  • 是的。我只是在我的团队里谈论这个。从长远来看,我可能会将我们的代码移植到 REST,但是有很多调用,当 WCF 工作得很好时,遇到所有这些麻烦似乎很荒谬。不过,当您说不推荐已知类型时,我不知道您的意思。已知类型是 WCF 序列化的核心。

标签: c# android .net wcf xamarin


【解决方案1】:

我对自己的问题有一个答案。它盯着我看了很长时间,很简单。

我不知道为什么,但我们通常被鼓励使用 OperationContract 的参数来指定已知类型。在大约 8 年的时间里,我认为这是做到这一点的唯一方法。我一直都知道可以在使用 DataContractSerializer 手动序列化/反序列化时指定已知类型,但我不知道如何覆盖 WCF 的默认功能来实例化我指定的 DataContractSerializer。

无论如何,还有另一种方法,而且非常简单。您只需将列表或检索类型列表的接口传递给代理的构造函数,然后手动将类型添加到操作中,如下所示:

这适用于 .NET Standard 代理(基于任务的操作)

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);
            }
        }
    }
}

【讨论】:

    猜你喜欢
    • 2016-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多