如何以编程方式配置 WCF 已知类型?
Posted
技术标签:
【中文标题】如何以编程方式配置 WCF 已知类型?【英文标题】:How do you configure WCF known types programmatically? 【发布时间】:2010-10-20 18:29:24 【问题描述】:我的客户端/服务器应用程序正在使用 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>
相反,我想做这样的事情:
foreach (Type type in _transmittedTypes)
// How would I write this method?
AddKnownType(typeof(MyParent), type);
有人可以解释一下我该怎么做吗?
编辑请理解,我试图在运行时动态设置已知类型,而不是在配置中以声明方式或在源代码中使用属性。
这基本上是关于 WCF API 的问题,而不是样式问题。
EDIT 2 This MSDN page 页面状态:
您还可以将类型添加到 ReadOnlyCollection,通过 DataContractSerializer 的 KnownTypes 属性访问。
不幸的是,这就是它所说的全部内容,并且鉴于 KnownTypes 是一个只读属性,并且该属性的值为ReadOnlyCollection
,这并没有多大意义。
【问题讨论】:
在您的编辑 2 中:我猜他们的意思是您可以通过 DataContractSerializer 构造函数传入额外的已知类型。不过,这对您的情况没有多大帮助,因为 WCF 自己制作了它的序列化程序。 【参考方案1】:将[ServiceKnownType]
添加到您的[ServiceContract]
界面:
[ServiceKnownType("GetKnownTypes", typeof(KnownTypesProvider))]
然后创建一个名为KnownTypesProvider
的类:
internal static class KnownTypesProvider
public static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider provider)
// collect and pass back the list of known types
然后你可以传回你需要的任何类型。
【讨论】:
也许我的问题不清楚。这不是“以编程方式” - 它仍然是声明性的。我需要能够在运行时添加已知类型,而不是获取它们。 @Drew Noakes - 嗯?在 GetKnownTypes 方法中,这只是代码,您可以在该时间点返回已知类型。该属性只是用来告诉 WCF 调用什么方法来获取已知类型。我认为,这就像你可以在 WCF 中以编程方式使用它一样(缺少以编程方式编辑配置文件并重新加载它)。 我很抱歉。这实际上是一个有效的答案,我只是将其误读为访问现有已知类型的一种方式。再看一遍,我不知道我在想什么。 只是想再次插话,非常感谢您的回答。我刚刚开始使用它,它第一次完美运行。我稍微编辑了您的答案以将返回类型扩大到IEnumerable<Type>
,并表明该类可以是内部的并且仍然可以正常工作。再次感谢。
参数“ICustomAttributeProvider provider”的作用是什么?【参考方案2】:
还有 2 种额外的方法可以解决您的问题:
我。使用 KnownTypeAttribute(string):
[DataContract]
[KnownType("GetKnownTypes")]
public abstract class MyParent
static IEnumerable<Type> GetKnownTypes()
return new Type[] typeof(MyChild1), typeof(MyChild2), typeof(MyChild3) ;
二。使用构造函数 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 ...
注意:您必须在双方都使用此属性:客户端和服务端!
【讨论】:
太棒了!我一直在寻找这个代码大约 8 年!不幸的是,我不确定它是否适用于我想要实现的所有平台。【参考方案3】:我需要这样做才能让继承正常工作。我不想维护派生类型的列表。
[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();
我知道函数的第一行是多余的,但这只是意味着我可以将它粘贴到任何基类中而无需修改。
【讨论】:
很好,谢谢。但我认为 ToListWeb .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;
【讨论】:
参数“ICustomAttributeProvider provider”的作用是什么?需要吗? @MichaelFreidgeim:我认为这是必需的,不是由于接口,而是由于使用反射的内部实现。通常在序列化回调等中。【参考方案5】:有点矫枉过正,但可以工作并且是一种未来的证明
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);
【讨论】:
以上是关于如何以编程方式配置 WCF 已知类型?的主要内容,如果未能解决你的问题,请参考以下文章