Как настроить процесс, используемый WCF при сериализации аргументов метода контракта? - PullRequest
4 голосов
/ 13 июня 2010

Я хотел бы сформулировать надуманный сценарий, который, тем не менее, имеет твердую фактическую основу.Представьте себе тип коллекции COuter, который является оберткой вокруг экземпляра другого типа коллекции CInner.Оба реализуют IList (не говоря уже о T).

Кроме того, экземпляр COuter скрыт внутри некоторого графа объектов, корень которого (будем называть его R) возвращается из метода службы WCF.

Мой вопрос: как я могу настроить процесс сериализации WCF, чтобы при возврате R запрос на сериализацию экземпляра COuter был направлен через мой код, который извлечет CInner и вместо этого передаст его сериализатору,Таким образом, принимающая сторона все еще получает R, только экземпляр COuter не найден в графе объектов.

Я надеялся, что Как WCF сериализует вызов метода? будет содержать ответ, к сожалению, статьяупомянуто там (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
  • Клиент - aпростой консольный клиент.

Служба определяет один метод, который возвращает экземпляр типа HelloServiceResult, который содержит ссылку на COuterList тип, который упаковывает CInnerList тип.Ссылка указывается как IMyListInterface, где и COuterList, и CInnerList реализуют этот интерфейс.Мне нужно, чтобы при сериализации результата перед его передачей клиенту ссылка COuterList была заменена на завернутую ссылку CInnerList.Я знаю, что это можно сделать, используя существующие возможности WCF, я просто не знаю, как.

Ответы [ 2 ]

8 голосов
/ 23 июня 2010

Вот как вы реализуете свой собственный суррогат:

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/

0 голосов
/ 20 июня 2010

Вы пробовали старый добрый 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);
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...