.net 如何以编程方式配置 WCF 已知类型?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/771560/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do you configure WCF known types programmatically?
提问by Drew Noakes
My client/server application is using WCF for communication, which has been great. However one shortcoming of the current architecture is that I must use known type configuration for certain transmitted types. I'm using an in-house Pub/Sub mechanism and this requirement is unavoidable.
我的客户端/服务器应用程序使用 WCF 进行通信,这很棒。然而,当前架构的一个缺点是我必须对某些传输类型使用已知类型配置。我使用的是内部发布/订阅机制,这个要求是不可避免的。
The problem is that it's easy to forget to add the known type, and if you do, WCF fails silently with few clues as to what's going wrong.
问题是很容易忘记添加已知类型,如果你这样做了,WCF 会默默地失败,几乎没有关于出了什么问题的线索。
In my application, I know the set of types that are going to be sent. I would like to perform the configuration programmatically, rather than declaratively through the App.configfile which currently contains something like this:
在我的应用程序中,我知道将要发送的类型集。我想以编程方式执行配置,而不是通过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>
Instead, I'd like to do something like this:
相反,我想做这样的事情:
foreach (Type type in _transmittedTypes)
{
// How would I write this method?
AddKnownType(typeof(MyParent), type);
}
Can someone please explain how I might do this?
有人可以解释一下我如何做到这一点吗?
EDITPlease understand that I'm trying to set the known types dynamically at run time rather than declaratively in config or using attributes in the source code.
编辑请理解,我试图在运行时动态设置已知类型,而不是在配置中声明性地设置或在源代码中使用属性。
This is basically a question about the WCF API, not a style question.
这基本上是一个关于 WCF API 的问题,而不是一个样式问题。
EDIT 2This MSDN pagepage states:
编辑 2此 MSDN页面说明:
You can also add types to the ReadOnlyCollection, accessed through the KnownTypes property of the DataContractSerializer.
您还可以向 ReadOnlyCollection 添加类型,通过 DataContractSerializer 的KnownTypes 属性访问。
Unfortunately that's all it says and it doesn't make terribly much sense given that KnownTypes is a readonly property, and the property's value is a ReadOnlyCollection.
不幸的是,这就是它所说的全部内容,鉴于KnownTypes 是一个只读属性,并且该属性的值是一个ReadOnlyCollection.
回答by Miki Watts
Add [ServiceKnownType]to your [ServiceContract]interface:
添加[ServiceKnownType]到您的[ServiceContract]界面:
[ServiceKnownType("GetKnownTypes", typeof(KnownTypesProvider))]
then create a class called KnownTypesProvider:
然后创建一个名为的类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 back whatever types you need.
然后你可以传回你需要的任何类型。
回答by Sergey Teplyakov
There are 2 additional ways to solve your problem:
还有 2 种其他方法可以解决您的问题:
I. Use KnownTypeAttribute(string):
一、使用KnownTypeAttribute(string):
[DataContract]
[KnownType("GetKnownTypes")]
public abstract class MyParent
{
static IEnumerable<Type> GetKnownTypes()
{
return new Type[] { typeof(MyChild1), typeof(MyChild2), typeof(MyChild3) };
}
}
II. Use constructor DataContractSerializer
二、使用构造函数 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: client side and service side!
注意:您必须在两端使用此属性:客户端和服务端!
回答by Paul McNamara
I needed to do this to allow inheritance to work properly. I didn't want to have to maintain the 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 the first line of the function is overkill but it just means I can paste it into any base class without modification.
我知道函数的第一行有点矫枉过正,但这只是意味着我可以将它粘贴到任何基类中而无需修改。
回答by Amit Bagga
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);
}
The Order is the abstract base class
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; }
}
回答by aeroson
a bit overkill, but works and is 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);

