如果[ServiceContract] [OperationContract]
指定作为接口的返回值(IInterface
),那么我可以返回一个Base对象(Base : IInterface
)或一个Derived对象(Derived : Base
)在服务器接口上使用ServerKnownType属性。
但是,如果我需要将Derived对象作为Base对象传输(因为Derived添加了服务器端功能,我不需要客户端),那么我卡住了。
派生类技巧[DataContract(Name = "Base")]
(参见here)在这种情况下(服务器方法返回接口)不起作用,因为我们必须将Derived对象和Base对象声明为KnownTypes - 和当服务器来解析要序列化的类型时,它不喜欢具有相同数据协定名称的2种不同的已知类型 - 并且在解析时将抛出异常。
如何在这种情况下转发派生为基地?
答案 0 :(得分:0)
您可以使用以下DataContractResolver将[DataContract(Name =“Base”)]的使用扩展为Derived上的属性,以指定它应序列化为Base,以便在服务器接口方法返回接口而不是混凝土基础类型。
要使用它,必须将Base声明为KnownType(ServerKnownType),并且Derived必须具有[DataContract(Name =“Base”)]属性,但不能声明为KnownType。
public class DeserializeAsBaseResolver : DataContractResolver {
public static void Install(ServiceHost serviceHost) {
SetSerializationBehavior(serviceHost, "MethodReturningIInterfac");
}
public static void SetSerializationBehavior(ServiceHost serviceHost, string contractMethodName) {
ContractDescription cd = serviceHost.Description.Endpoints[0].Contract;
OperationDescription myOperationDescription = cd.Operations.Find(contractMethodName);
bool methodFound = (myOperationDescription != null);
if (!methodFound) {
string msg = string.Format("\"{0}\" not a valid method on {1}",
contractMethodName, cd.ConfigurationName);
throw new ArgumentException(msg);
}
DataContractSerializerOperationBehavior serializerBehavior =
myOperationDescription.Behaviors.Find<DataContractSerializerOperationBehavior>();
if (serializerBehavior == null) {
serializerBehavior = new DataContractSerializerOperationBehavior(myOperationDescription);
myOperationDescription.Behaviors.Add(serializerBehavior);
}
serializerBehavior.DataContractResolver = new DeserializeAsBaseResolver();
}
public override bool TryResolveType(Type type, Type declaredType,
DataContractResolver knownTypeResolver,
out XmlDictionaryString typeName,
out XmlDictionaryString typeNamespace) {
// Look for [DataContract(Name = "ClassType")] attribute and serialize as that
// type if one is found:
Type typeToResolveOn = type;
DataContractAttribute dca = null;
// .Net 4.0 and below code:
object[] attributes = type.GetCustomAttributes(typeof(DataContractAttribute), false);
foreach (object attibute in attributes) {
if (attibute is DataContractAttribute) {
dca = attibute as DataContractAttribute;
break;
}
}
// .Net 4.5 and above code:
//dca = type.GetCustomAttribute<DataContractAttribute>();
if (dca != null && !string.IsNullOrEmpty(dca.Name)) {
string modifiedAssemblyQualifiedName = string.Format("{0}.{1}, {2}", type.Namespace, dca.Name, type.Assembly);
Type serializeAsType = Type.GetType(modifiedAssemblyQualifiedName);
if (serializeAsType != null)
typeToResolveOn = serializeAsType;
}
bool ret = knownTypeResolver.TryResolveType(typeToResolveOn, declaredType, null, out typeName, out typeNamespace);
if (!ret && type != typeToResolveOn)
ret = knownTypeResolver.TryResolveType(type, declaredType, null, out typeName, out typeNamespace);
return ret;
}
public override Type ResolveName(string typeName, string typeNamespace,
Type declaredType, DataContractResolver knownTypeResolver) {
return knownTypeResolver.ResolveName(typeName, typeNamespace, declaredType, null) ?? declaredType;
}
}
}