How to configure WCF program types?

In my client / server application, WCF is used for communication, which was great. However, one drawback of the current architecture is that I have to use a well-known type configuration for certain passed types. I use my own Pub / Sub engine, and this requirement is inevitable.

The problem is that it's easy to forget to add a known type, and if you do, WCF will work without the slightest clue as to what is going wrong.

In my application, I know the set of types that will be sent. I would like to perform the configuration programmatically and not declaratively through the App.config , which currently contains something like this:

 <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> 

Instead, I would like to do something like this:

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

Can someone explain how I can do this?

EDIT Please understand that I am trying to dynamically set known types at runtime, and not declaratively in the configuration or using attributes in the source code.

This is mainly a question about the WCF API, not the style.

EDIT 2 The page on this MSDN page says:

You can also add types to ReadOnlyCollection, accessed through the KnownTypes DataContractSerializer property.

Unfortunately, all this says, and it does not make much sense, given that KnownTypes is a readonly property, and the value of the property is ReadOnlyCollection .

+48
wcf wcf-configuration known-types
Apr 21 '09 at 8:12
source share
5 answers

Add [ServiceKnownType] to your [ServiceContract] interface:

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

then create a class called KnownTypesProvider :

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

and then you can pass all the types you need.

+64
Apr 21 '09 at 8:25
source share

There are 2 additional ways to solve your problem:

I am. Use KnownTypeAttribute (string):

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

II. Use the constructor 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 {...} 

NOTE. You must use this attribute on both sides: on the client side and on the service side!

+16
Jan 20 '10 at 19:57
source share

I needed to do this to allow inheritance to work correctly. I did not want to maintain a list of derived 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(); } 

I know that the first line of the overkill function, but that just means that I can insert it into any base class without changes.

+13
Jan 28 '11 at 15:52
source share

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 is an abstract base class




 [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; } } 
+3
Dec 17 '10 at 0:22
source share

a little bust, but it works and is a kind of future proof

 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); 
0
Nov 04 '16 at 15:08
source share



All Articles