如何在序列化合同方法参数时自定义WCF使用的进程?

时间:2010-06-13 03:31:02

标签: wcf serialization

我想制定一个人为的方案,但它具有坚实的实际基础。想象一个集合类型COuter,它是另一个集合类型CInner的实例的包装器。两者都实现了IList(更别提了T)。

此外,COuter实例被隐藏在一些对象图中,其根(我们将其称为R)从WCF服务方法返回。

我的问题是如何自定义WCF序列化过程,以便在返回R时,序列化COuter实例的请求将通过我的代码路由,这将提取CInner并将其传递给序列化器。因此,接收端仍然获得R,只在对象图中找不到COuter实例。

我希望How does WCF serialize the method call?将包含答案,遗憾的是,那里提到的文章(http://msdn.microsoft.com/en-us/magazine/cc163569.aspx)几乎没有提到使用IDataContractSurrogate接口可以实现高级序列化方案,但没有给出详细信息。另一方面,我真的很想看到一个有效的例子。

非常感谢你。

修改

我创建了一个简单的WCF示例,它演示了这个问题。存档位于此处 - https://docs.google.com/leaf?id=0B2pbsdBJxJI3NzFiNjcxMmEtMTM5Yy00MWY2LWFiMTUtNjJiNjdkYTU1ZTk4&sort=name&layout=list&num=50

它包含三个小项目:

  • HelloServiceAPI - 包含服务接口和参数类型
  • 主机 - HelloService主机
  • 客户端 - 一个简单的控制台客户端。

该服务定义了一个方法,该方法返回HelloServiceResult类型的实例,其中包含对COuterList类型的引用,该类型包含CInnerList类型。该引用被指定为IMyListInterface,其中COuterListCInnerList都实现了此接口。我需要的是,当结果在传输到客户端之前被序列化时,COuterList引用将被包装的CInnerList引用替换。我知道这可以通过利用WCF的现有功能来完成,我只是不知道如何。

2 个答案:

答案 0 :(得分:8)

以下是您实施自己的代理人的方法:

class YourCustomTypeSurrogate : IDataContractSurrogate
{
    public Type GetDataContractType(Type type)
    {
        // Just for reference
        //if (typeof(OldType).IsAssignableFrom(type))
        //{
        //    return typeof(NewType);
        //}
        return type;
    }
    public object GetObjectToSerialize(object obj, Type targetType)
    {
        // This method is called on serialization.
        //if (obj is OldType)
        //{
        //    // ... use the XmlSerializer to perform the actual serialization.
        //    NewType newObj = new NewType();
        //    return newObj;
        //}
        return obj;
    }
    public object GetDeserializedObject(object obj, Type targetType)
    {
        // This method is called on deserialization.
        // If PersonSurrogated is being deserialized...
        //if (obj is NewType)
        //{
        //    OldType newObj = new OldType();
        //    return newObj;
        //}
        return obj;
    }
    public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
    {
        // This method is called on schema import.

        //if (typeNamespace.Equals("Your Type Namespace"))
        //{
        //    if (typeName.Equals("NewType"))
        //    {
        //        return typeof(OldType);
        //    }
        //}
        return null;
    }

    public System.CodeDom.CodeTypeDeclaration ProcessImportedType(System.CodeDom.CodeTypeDeclaration typeDeclaration, System.CodeDom.CodeCompileUnit compileUnit)
    {
        // Not used in this sample.
        // You could use this method to construct an entirely 
        // new CLR type when a certain type is imported, or modify a generated
        // type in some way.
        return typeDeclaration;
    }


    public object GetCustomDataToExport(Type clrType, Type dataContractType)
    {
        // Not used in this sample
        return null;
    }

    public object GetCustomDataToExport(System.Reflection.MemberInfo memberInfo, Type dataContractType)
    {
        // Not used in this sample
        return null;
    }

    public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)
    {
        // Not used in this sample
    }
}

然后创建自定义序列化程序操作行为:

public class CustomDataContractSerializerOperationBehavior : DataContractSerializerOperationBehavior
{
    public CustomDataContractSerializerOperationBehavior(OperationDescription operationDescription) : base(operationDescription) { }

    public override XmlObjectSerializer CreateSerializer(Type type, string name, string ns, IList<Type> knownTypes)
    {
        return new DataContractSerializer(
            type /*typeof OldType*/,
            knownTypes,
            int.MaxValue /*maxItemsInObjectGraph */,
            false /*ignoreExtensionDataObject*/,
            true /*preserveObjectReferences*/,
            new YourCustomTypeSurrogate());
    }

    public override XmlObjectSerializer CreateSerializer(Type type, XmlDictionaryString name, XmlDictionaryString ns, IList<Type> knownTypes)
    {
        return new DataContractSerializer(
            type /*typeof OldType*/,
            knownTypes,
            int.MaxValue /*maxItemsInObjectGraph */,
            false /*ignoreExtensionDataObject*/,
            true /*preserveObjectReferences*/,
            new YourCustomTypeSurrogate());
    }
}

之后,您创建一个属性以将上述操作行为应用于操作合同:

public class CustomDataContractFormatAttribute : Attribute, IOperationBehavior
{
    public void AddBindingParameters(OperationDescription description, BindingParameterCollection parameters)
    {
    }

    public void ApplyClientBehavior(OperationDescription description, ClientOperation proxy)
    {
        ReplaceDataContractSerializerOperationBehavior(description);
    }

    public void ApplyDispatchBehavior(OperationDescription description, DispatchOperation dispatch)
    {
        ReplaceDataContractSerializerOperationBehavior(description);
    }

    public void Validate(OperationDescription description)
    {
    }

    private static void ReplaceDataContractSerializerOperationBehavior(OperationDescription description)
    {
        DataContractSerializerOperationBehavior dcs = description.Behaviors.Find<DataContractSerializerOperationBehavior>();

        if (dcs != null)
            description.Behaviors.Remove(dcs);

        description.Behaviors.Add(new CustomDataContractSerializerOperationBehavior(description));
    }
}

最后,将此属性应用于操作:

    [OperationContract]
    [CustomDataContractFormat]
    void DoWork();

如果要将此应用于整个服务,则可以自定义“服务行为”而不是“操作行为”。

以下是用于创建此示例的参考:

http://msdn.microsoft.com/en-us/library/system.runtime.serialization.idatacontractsurrogate.aspx

http://www.danrigsby.com/blog/index.php/2008/03/07/xmlserializer-vs-datacontractserializer-serialization-in-wcf/

http://www.danrigsby.com/blog/index.php/2008/04/10/specifying-a-different-serializer-per-endpoint-in-wcf/

http://social.msdn.microsoft.com/forums/en-US/wcf/thread/e4d55f3f-86d1-441d-9187-64fbd8ab2b3d/

答案 1 :(得分:0)

你有没有尝试过好的OnSerializingAttribute

[Serializable]
[KnownType(typeof(COuterList))]
public class HelloServiceResult
{
    public IMyListInterface List;

    [OnSerialized]
    void OnSerializing(StreamingContext context)
    {
        if (List is COuterList)
        {
            List = ((List as COuterList).InnerList as CInnerList);
        }
    }
}