WCF - 指定已知类型(PCL / .NET标准)

时间:2016-09-09 01:37:34

标签: c# android .net wcf xamarin

这个问题又出现了。这不是.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标准库没有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)
    {
    }
}

那么,我们有什么选择?

1 个答案:

答案 0 :(得分:0)

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

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

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

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

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