Сбой десериализации C # в надстройке автоматизации, но не в тестах NUnit - PullRequest
0 голосов
/ 27 июля 2011

У меня есть надстройка автоматизации (реализующая Extensibility.IDTExtensibility2), написанная на C # 4, в которой я пытаюсь загрузить некоторые (двоичные) сериализованные данные. Он отлично работает в модульных тестах, но не работает при запуске из Excel:

Unable to find assembly 'XXX, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
   at System.Runtime.Serialization.Formatters.Binary.BinaryAssemblyInfo.GetAssembly()
   at System.Runtime.Serialization.Formatters.Binary.ObjectReader.GetType(BinaryAssemblyInfo assemblyInfo, String name)
   at System.Runtime.Serialization.Formatters.Binary.ObjectMap..ctor(String objectName, String[] memberNames, BinaryTypeEnum[] binaryTypeEnumA, Object[] typeInformationA, Int32[] memberAssemIds, ObjectReader objectReader, Int32 objectId, BinaryAssemblyInfo assemblyInfo, SizedArray assemIdToAssemblyTable)
   at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.ReadObjectWithMapTyped(BinaryObjectWithMapTyped record)
   at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.ReadObjectWithMapTyped(BinaryHeaderEnum binaryHeaderEnum)
   at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.Run()
   at System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler handler, __BinaryParser serParser, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
   at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
   at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream)
   at 

На BinaryFormatter я устанавливаю AssemblyFormat (для сериализации и десериализации) следующим образом:

serializationCodec.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;

То, что я думал, будет игнорировать версию согласно здесь .

Затем подумал, что это может быть связано с тем, что в Excel указано «Надежное расположение», поэтому я добавил каталог проекта и проверил все подкаталоги, но ошибка осталась.

Напрасно я пытался добавить атрибут System.Runtime.Serialization.OptionalFieldAttribute, но это не помогло.

Модульные тесты могут загружать сериализованные файлы, сгенерированные самим собой, или тот же код, выполненный в Excel, но Excel не может загрузить сериализованные данные независимо от того, была ли выполнена фактическая сериализация.

Тот факт, что Excel не может десериализовать то, что он сериализовал сам, намекает на то, что это редерринг, поскольку он, очевидно, будет иметь доступ к используемой сборке.

Итак, вопрос в том, почему Excel десериализуется иначе, чем мой модульный тест? (Или, может быть, более важно; как я могу заставить десериализацию работать в Excel?)

спасибо.

1 Ответ

0 голосов
/ 07 октября 2011

Я преследовал красную сельдь (и) и не нашел места, предполагая, что это была проблема с версиями (я смотрел на прокладки и все виды).

Это было на самом деле из-за странной сборкисвязывание;хотя DLL-библиотека надстройки могла видеть и загружать классы из тех DLL-библиотек, от которых она зависела, при возможности десериализации она не могла найти необходимые сборки - даже если вызов десериализации был из класса, принадлежащего к DLL, которая, по ее утверждению, не могланайди!

Решение - это, конечно, хак, но оно заставило меня преодолеть это, поэтому, если кто-то застрянет, как я, вот хак (ы):

Внутри класса X этозависит от классов Y и Z, каждый из которых имеет свои соответствующие DLL проекта (названные ProjectForX, ProjectForY и ProjectForZ)

private static Assembly MyResolveEventHandler(object sender, ResolveEventArgs args)
    {
        if(args.Name.StartsWith("ProjectForX,")) {
            return typeof(X).Assembly;
        } else if(args.Name.StartsWith("ProjectForY,")) {
            return typeof(Y).Assembly;
        } else if(args.Name.StartsWith("ProjectForZ,")) {
            return typeof(Z).Assembly;
        }
        return null;
    }


    public static X LoadX(string filename)
    {
        AppDomain currentDomain = AppDomain.CurrentDomain;
        ResolveEventHandler handler = new ResolveEventHandler(MyResolveEventHandler);
        currentDomain.AssemblyResolve += handler;
        try {
            Stream stream = new FileStream(@filename, FileMode.Open);
            try {
                BinaryFormatter deserializer = createBinaryFormatter();
                X model = (X)deserializer.Deserialize(stream);
                return model;
            } finally {
                stream.Close();
            }
        } finally {
            currentDomain.AssemblyResolve -= handler;
        }
    }

По сути, он просто подключается к событиям разрешения и обеспечивает правильную сборку на основе запрошенного имени типа - этоне особенно безопасен (я использую запятую в дескрипторе сборки, чтобы попытаться свести к минимуму любые конфликты).

Почти наверняка есть ясный способ сделать это, и проблема, вероятно, является просто результатом плохойконфигурация и мой ограниченный опыт работы с продуктами MS и C # в частности.

Это не было бы проблемой, если бы я только что построил монолитную DLL =)

...