如何以编程方式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);