如何以编程方式configurationWCF已知types?

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

问题是,忘记添加已知types很容易,如果你这样做的话,WCF会自动地失败,并且会发生什么错误的线索。

在我的应用程序中,我知道将要发送的一组types。 我想以编程方式执行configuration,而不是通过当前包含如下内容的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> 

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

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

有人可以解释我怎么可以做到这一点?

编辑请理解,我试图在运行时dynamic设置已知types,而不是在configuration中使用声明或在源代码中使用属性。

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

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

您还可以将types添加到ReadOnlyCollection,通过DataContractSerializer的KnownTypes属性访问。

不幸的是,这就是所有这一切,因为KnownTypes是一个只读属性,并且属性值是一个ReadOnlyCollection ,所以它并没有太大的意义。

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

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

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

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

然后你可以传回你需要的任何types。

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

I.使用KnownTypeAttribute(string):

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

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

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

我需要这样做才能使inheritance正常工作。 我不想维护派生types的列表。

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

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

Web .Config

 <applicationSettings> <HostProcess.Properties.Settings> <setting name="KnowTypes" serializeAs="Xml"> <value> <ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <string>a.AOrder,a</string> <string>b.BOrder,b</string> <string>c.COrder,c</string> </ArrayOfString> </value> </setting> </HostProcess.Properties.Settings> 
 static class Helper { public static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider provider) { System.Collections.Generic.List<System.Type> knownTypes = new System.Collections.Generic.List<System.Type>(); // Add any types to include here. Properties.Settings.Default.KnowTypes.Cast<string>().ToList().ForEach(type => { knownTypes.Add(Type.GetType(type)); }); return knownTypes; } } [ServiceContract] [ServiceKnownType("GetKnownTypes", typeof(Helper))] public interface IOrderProcessor { [OperationContract] string ProcessOrder(Order order); } 

Order是抽象基类


 [DataContract] public abstract class Order { public Order() { OrderDate = DateTime.Now; } [DataMember] public string OrderID { get; set; } [DataMember] public DateTime OrderDate { get; set; } [DataMember] public string FirstName { get; set; } [DataMember] public string LastName { get; set; } } 

有点矫枉过正,但是起作用,并且是未来的certificate

 var knownTypes = AppDomain.CurrentDomain .GetAssemblies() .Where(a => { var companyAttribute = a.GetCustomAttribute<AssemblyCompanyAttribute>(); if (companyAttribute == null) return false; return companyAttribute.Company.ToLower().Contains("[YOUR COMPANY NAME]"); }) .SelectMany(a => a.GetTypes()).Where(t => t.IsSerializable && !t.IsGenericTypeDefinition); var serializer = new DataContractSerializer(type, knownTypes);