Списки IronPython, кортежи, словари завершают работу с сообщениями WCF - PullRequest
2 голосов
/ 17 декабря 2011

Я пытаюсь использовать WCF для удаленного выполнения IronPython в C #. Все в моей системе функционирует прекрасно, пока оно локально.

Я выделил проблему для передачи определенных объектов клиенту через WCF:

Если вы попытаетесь передать их клиенту WCF с сервера WCF, произойдет сбой канала связи:

  1. PythonDictionaries, содержащие значения, которые являются кортежами или списками
  2. Кортежи любого типа

... Странно, словари, содержащие словари, в порядке (если вложенный словарь не удовлетворяет этим двум условиям). Вот мой пример кода:

try
{
    PythonFlow localPython = new PythonFlow();
    IPythonFlow remotePython = new IronTesterWcfClient("localhost", "8000");

    string tuple = "(1,2,3)";
    string list = "[1,2,3]";
    string complexDict0 = "{'a':'b','c':{'d':'f'}}";
    string complexDict1 = "{'a':'b','c':(1,2,3),'e':'e'}";
    string complexDict2 = "{'a':'b','c':[1,2,3],'d':'e'}"; 
    string complexDict3 = "{'a':'b','c':[1,2,3],'d':(1,2,3),'e':{'a':'b','c':[1,2,3],'d':(1,2,3)}}";

    localPython.OpenFlow(args[2]);
    //OK
    IronPython.Runtime.List list1 = localPython.PythonListFromString(list);
    //OK
    IronPython.Runtime.PythonDictionary dict0 = localPython.PythonDictionaryFromString(complexDict0);
    //OK
    IronPython.Runtime.PythonDictionary dict1 = localPython.PythonDictionaryFromString(complexDict1);
    //OK
    IronPython.Runtime.PythonDictionary dict2 = localPython.PythonDictionaryFromString(complexDict2);
    //OK
    IronPython.Runtime.PythonDictionary dict3 = localPython.PythonDictionaryFromString(complexDict3);
    //OK
    IronPython.Runtime.PythonTuple tuple1 = localPython.PythonTupleFromString(tuple);

    remotePython.OpenFlow(args[2]);
    //OK
    IronPython.Runtime.List list2 = remotePython.PythonListFromString(list);
    //OK
    IronPython.Runtime.PythonDictionary dict5 = remotePython.PythonDictionaryFromString(complexDict0);
    //Fail!!!
    IronPython.Runtime.PythonDictionary dict6 = remotePython.PythonDictionaryFromString(complexDict1);
    //Fail!!!
    IronPython.Runtime.PythonDictionary dict7 = remotePython.PythonDictionaryFromString(complexDict2);
    //Fail!!!
    IronPython.Runtime.PythonDictionary dict8 = remotePython.PythonDictionaryFromString(complexDict3);
    //Fail!!!
    IronPython.Runtime.PythonTuple tuple2 = remotePython.PythonTupleFromString(tuple);
}
catch (Exception ex)
{
    //The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication because it is in the Faulted state.
    Console.WriteLine(ex.ToString());
}

Я использую NetTcpBinding с SecurityMode.None на стороне сервера WCF ... Я должен также упомянуть, что вызов python в конечном итоге обращается к простому объекту в python, который возвращает результат eval ()

В основном это делает невозможным использование Python с WCF. Есть идеи?

Подробнее ... Мне наконец удалось извлечь исключения из WCF, когда это произошло:

Внешнее исключение:

Произошла ошибка при попытке сериализации параметра http://Intel.ServiceModel.Samples:TestResult.
Сообщение InnerException было «Тип» IronPython.Runtime.PythonTuple »с именем контракта данных« ArrayOfanyType: http://schemas.microsoft.com/2003/10/Serialization/Arrays' не ожидается. Рассмотрите возможность использования DataContractResolver или добавьте любые типы, которые не известны статически, в список известных типов - например, с помощью атрибута KnownTypeAttribute или добавив их в список известных типов, переданных в DataContractSerializer. '. Пожалуйста, смотрите InnerException для более подробной информации.

Внутреннее исключение:

Введите 'IronPython.Runtime.PythonTuple' с именем контракта данных 'ArrayOfanyType: http://schemas.microsoft.com/2003/10/Serialization/Arrays' не ожидается. Рассмотрите возможность использования DataContractResolver или добавьте любые типы, которые не известны статически, в список известных типов - например, с помощью атрибута KnownTypeAttribute или добавив их в список известных типов, передаваемых в DataContractSerializer.

1 Ответ

0 голосов
/ 20 декабря 2011

Вы получаете SerializationException, означающее, что .NET не знает, как десериализовать часть данных, которые вы отправляете.В этом случае он захлебывается ArrayOfanyType, который является любым видом неуниверсальной коллекции (например, ArrayList или простой массив).

Я рассмотрел источник для IronPython 2.7.1(какую версию вы используете?), глядя на реализацию List и PythonTuple.Оба содержат массив Object, в значительной степени идентичный объявленному;В List есть несколько других случайных полей экземпляров.

// IronPython.Runtime.List
internal volatile object[] _data;
private const int INITIAL_SIZE = 20;
internal int _size;

// IronPython.Runtime.PythonTuple
internal readonly object[] _data;

Я не знаю, почему сериализатор недоволен классом PythonTuple, когда он нормально работает с List.Однако это, вероятно, указывает на то, что средство распознавания типов .NET не может разрешить какой-либо элемент сериализованного объекта.

Существует два способа решения этой проблемы, о которых я знаю.

  1. Вы можете попытаться убедить .NET учитывать данный тип во время десериализации, используя атрибут KnownTypes MSDN :

    Когда данные поступают в принимающую конечную точку, среда выполнения WCF пытается десериализовать данные в экземпляр общеязыкового типа среды выполнения (CLR).Тип, который создается для десериализации, выбирается сначала проверкой входящего сообщения, чтобы определить контракт данных, которому соответствует содержимое сообщения.Затем механизм десериализации пытается найти тип CLR, который реализует контракт данных, совместимый с содержимым сообщения.Набор типов кандидатов, который механизм десериализации допускает во время этого процесса, называется набором «известных типов» десериализатора.

    Вы хотите применить этот атрибут к классу, передаваемому черезпровод, и это не удобно, когда вы не контролируете класс, как здесь.Так что это, вероятно, не стартер.

  2. Вы можете указать пользовательский DataContractResolver для разрешения проблемных типов:

    Средство сопоставления контрактов данных позволяет динамически настраивать известные типы.Известные типы требуются при сериализации или десериализации типа, не ожидаемого контрактом данных.

    Вы можете сделать это, не управляя сериализуемым классом, но это требует немного больше работы. Это сообщение в блоге MSDN имеет отличную запись.

В итоге вы создадите DataContractResolver и переопределите два его метода, TryResolveType и ResolveName.Первый используется во время сериализации, а второй - при десериализации.Из примера MSDN с моими комментариями:

public class MyCustomerResolver : DataContractResolver
{
    public override bool TryResolveType(Type dataContractType, Type declaredType, DataContractResolver knownTypeResolver, out XmlDictionaryString typeName, out XmlDictionaryString typeNamespace)
    {
        if (dataContractType == typeof(Customer)) // a type I recognize
        {
            XmlDictionary dictionary = new XmlDictionary();
            typeName = dictionary.Add("SomeCustomer");
            typeNamespace = dictionary.Add("www.FPSTroller.com");
            return true;
        }
        else  // I don't know what this is; defer to the inbuilt type resolver
        {
            return knownTypeResolver.TryResolveType(dataContractType, declaredType, null, out typeName, out typeNamespace);
        }
    }

    public override Type ResolveName(string typeName, string typeNamespace, DataContractResolver knownTypeResolver)
    {
        // my type
        if (typeName == "SomeCustomer" && typeNamespace == "http://www.FPSTroller.com")
        {
            return typeof(Customer);
        }
        else // I don't know what this is; defer to the inbuilt type resolver
        {
            return knownTypeResolver.ResolveName(typeName, typeNamespace, null);
        }
    }
}

В сообщении блога, о котором я упоминал выше, есть некоторые примеры распознавателей, которые могут дать .NET лучший шанс и обрабатывать ваши классы без написания ничего особенного (ищите " Полезные распознаватели"заголовок).

Вы бы использовали DataContractSerializerOperationBehavior.Подключить ваш резольвер в WCF;см. пример в документации MSDN .

Наконец, прежде чем идти по этому пути, вы можете подумать об изменении интерфейса операций WCF.Вам действительно нужно передавать эти нестандартные типы по проводам?То, что я прочитал, подразумевает, что неуниверсальные типы часто сталкиваются с такой проблемой.Попробуйте использовать старый System.Collections.Generic.Dictionary<K,V> и (если вы используете .NET 4+) System.Tuple.Зафиксируйте ваши типы;не пытайся угадать решатель.

...