如何以编程方式配置WCF已知类型?

Dre*_*kes 52 .net wcf wcf-configuration known-types

我的客户端/服务器应用程序正在使用WCF进行通信,这非常棒.然而,当前架构的一个缺点是我必须对某些传输类型使用已知类型配置.我正在使用内部的Pub/Sub机制,这个要求是不可避免的.

问题是很容易忘记添加已知类型,如果你这样做,WCF会无声地失败,只有很少的线索可以解决出错的问题.

在我的应用程序中,我知道将要发送的类型集.我想以编程方式执行配置,而不是声明性地通过App.config当前包含这样的内容的文件:

<system.runtime.serialization>
  <dataContractSerializer>
    <declaredTypes>
      <add type="MyProject.MyParent, MyProjectAssembly">
        <knownType type="MyProject.MyChild1, MyProjectAssembly"/>
        <knownType type="MyProject.MyChild2, MyProjectAssembly"/>
        <knownType type="MyProject.MyChild3, MyProjectAssembly"/>
        <knownType type="MyProject.MyChild4, MyProjectAssembly"/>
        <knownType type="MyProject.MyChild5, MyProjectAssembly"/>
      </add>
    </declaredTypes>
  </dataContractSerializer>
</system.runtime.serialization>
Run Code Online (Sandbox Code Playgroud)

相反,我想做这样的事情:

foreach (Type type in _transmittedTypes)
{
    // How would I write this method?
    AddKnownType(typeof(MyParent), type);
}
Run Code Online (Sandbox Code Playgroud)

有人可以解释一下我怎么做这个吗?

编辑请理解我正在尝试在运行时动态设置已知类型,而不是在配置中使用声明或使用源代码中的属性.

这基本上是关于WCF API的问题,而不是样式问题.

编辑2 此MSDN页面指​​出:

您还可以向ReadOnlyCollection添加类型,通过DataContractSerializer的KnownTypes属性访问.

不幸的是,只要KnownTypes是一个只读属性,并且属性的值是a,它就会说并且它没有多大意义ReadOnlyCollection.

Mik*_*tts 69

添加[ServiceKnownType]到您的[ServiceContract]界面:

[ServiceKnownType("GetKnownTypes", typeof(KnownTypesProvider))]
Run Code Online (Sandbox Code Playgroud)

然后创建一个名为的类KnownTypesProvider:

internal static class KnownTypesProvider
{
    public static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider provider)
    {
         // collect and pass back the list of known types
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以传回你需要的任何类型.

  • @Drew Noakes - 嗯?在GetKnownTypes方法(只是代码)中,您可以在该时间点返回已知类型.该属性只是告诉WCF调用什么方法来获取已知类型.这是编程方式,你可以在WCF中拥有它,我认为(没有以编程方式编辑配置文件并重新加载它). (7认同)
  • 我道歉.这实际上是一个有效的答案,我只是把它误读为一种访问现有已知类型的方法.再看一遍,我不知道我在想什么. (4认同)
  • 参数"ICustomAttributeProvider provider"的目的是什么? (4认同)

Ser*_*kov 17

还有2种方法可以解决您的问题:

I.使用KnownTypeAttribute(string):

[DataContract]
[KnownType("GetKnownTypes")]
public abstract class MyParent
{
    static IEnumerable<Type> GetKnownTypes()
    {
        return new Type[] { typeof(MyChild1), typeof(MyChild2), typeof(MyChild3) };
    }
}
Run Code Online (Sandbox Code Playgroud)

II.使用构造函数DataContractSerializer

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | 
                AttributeTargets.Interface)]
public class MyHierarchyKnownTypeAttribute : Attribute, IOperationBehavior, IServiceBehavior, IContractBehavior
{
    private void IOperationBehavior.AddBindingParameters(
            OperationDescription description, 
            BindingParameterCollection parameters)
    {
    }

    void IOperationBehavior.ApplyClientBehavior(
            OperationDescription description, 
            ClientOperation proxy)
    {
        ReplaceDataContractSerializerOperationBehavior(description);
    }

    private void IOperationBehavior.ApplyDispatchBehavior(
            OperationDescription description, 
            DispatchOperation dispatch)
    {
        ReplaceDataContractSerializerOperationBehavior(description);
    }

    private void IOperationBehavior.Validate(OperationDescription description)
    {
    }

    private void IServiceBehavior.AddBindingParameters(
          ServiceDescription serviceDescription,
          ServiceHostBase serviceHostBase,
          System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints,
          BindingParameterCollection bindingParameters)
    {
        ReplaceDataContractSerializerOperationBehavior(serviceDescription);
    }

    private void IServiceBehavior.ApplyDispatchBehavior(
            ServiceDescription serviceDescription, 
            ServiceHostBase serviceHostBase)
    {
        ReplaceDataContractSerializerOperationBehavior(serviceDescription);
    }

    private void IServiceBehavior.Validate(ServiceDescription serviceDescription,
            ServiceHostBase serviceHostBase)
    {
    }

    private void IContractBehavior.AddBindingParameters(
            ContractDescription contractDescription,
            ServiceEndpoint endpoint, 
            BindingParameterCollection bindingParameters)
    {
    }

    private void IContractBehavior.ApplyClientBehavior(
            ContractDescription contractDescription,
            ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
        ReplaceDataContractSerializerOperationBehavior(contractDescription);
    }

    private void IContractBehavior.ApplyDispatchBehavior(
            ContractDescription contractDescription,
            ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
    {
        ReplaceDataContractSerializerOperationBehavior(contractDescription);
    }

    private void IContractBehavior.Validate(ContractDescription contractDescription,
            ServiceEndpoint endpoint)
    {
    }    

    private static void ReplaceDataContractSerializerOperationBehavior(
            ServiceDescription description)
    {
        foreach (var endpoint in description.Endpoints)
        {
            ReplaceDataContractSerializerOperationBehavior(endpoint);
        }
    }

    private static void ReplaceDataContractSerializerOperationBehavior(
            ContractDescription description)
    {
        foreach (var operation in description.Operations)
        {
            ReplaceDataContractSerializerOperationBehavior(operation);
        }
    }

    private static void ReplaceDataContractSerializerOperationBehavior(
            ServiceEndpoint endpoint)
    {
        // ignore mex
        if (endpoint.Contract.ContractType == typeof(IMetadataExchange))
        {
            return;
        }
        ReplaceDataContractSerializerOperationBehavior(endpoint.Contract);
    }

    private static void ReplaceDataContractSerializerOperationBehavior(
            OperationDescription description)
    {
        var behavior = 
         description.Behaviors.Find<DataContractSerializerOperationBehavior>();
        if (behavior != null)
        {
            description.Behaviors.Remove(behavior);
            description.Behaviors.Add(
                new ShapeDataContractSerializerOperationBehavior(description));
        }
    }

    public class ShapeDataContractSerializerOperationBehavior 
            : DataContractSerializerOperationBehavior
    {
        public ShapeDataContractSerializerOperationBehavior(
                OperationDescription description)
            : base(description) { }

        public override XmlObjectSerializer CreateSerializer(Type type, 
                string name, string ns, IList<Type> knownTypes)
        {
            var shapeKnownTypes = 
                new List<Type> { typeof(Circle), typeof(Square) };
            return new DataContractSerializer(type, name, ns, shapeKnownTypes);
        }

        public override XmlObjectSerializer CreateSerializer(Type type, 
                XmlDictionaryString name, XmlDictionaryString ns, 
                IList<Type> knownTypes)
        {
            //All magic here!
            var knownTypes = 
                new List<Type> { typeof(MyChild1), typeof(MyChild2), typeof(MyChild3) };
            return new DataContractSerializer(type, name, ns, knownTypes);
        }
    }
}

[ServiceContract()]
[MyHierarchyKnownTypeAttribute]
public interface IService {...}
Run Code Online (Sandbox Code Playgroud)

注意:您必须在双方使用此属性:客户端和服务端!


小智 13

我需要这样做才能让继承正常工作.我不想要维护派生类型列表.

 [KnownType("GetKnownTypes")]
 public abstract class BaseOperationResponse
 {

    public static Type[] GetKnownTypes()
    {
        Type thisType = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType;
        return thisType.Assembly.GetTypes().Where(t => t.IsSubclassOf(thisType)).ToArray();
    }
Run Code Online (Sandbox Code Playgroud)

我知道函数的第一行是矫枉过正,但它只是意味着我可以将其粘贴到任何基类中而无需修改.