Странное поведение при попытке получить тип по AssemblyQualifiedName во время выполнения

Я получаю исключение FileLoadException при попытке десериализации типа с помощью NetDataContractSerializer:

Указанное имя сборки или кодовая база недопустимы. (Исключение из HRESULT: 0x80131047)

Эта ошибка не имеет ничего общего с сериализатором; попытка загрузить тип во время выполнения по его квалифицированному имени сборки приводит к той же ошибке.

Я прикрепил слушателя к событию AssemblyResolve, чтобы посмотреть, что происходит:

ResolveEventHandler reh = (o, e) =>
{
    var tryGet = AppDomain.CurrentDomain.GetAssemblies()
                          .Where(x => x.FullName == e.Name).FirstOrDefault();
    if (tryGet != null)
        return tryGet;
    //EDIT:  Crap, the following line is a stupid bug STUPID!  Ignore!
    return Type.GetType(e.Name).Assembly;
};

using (var stream = System.IO.File.OpenRead(serializedObjectFilename))
{
    try
    {
        AppDomain.CurrentDomain.AssemblyResolve += reh;
        var ser = new NetDataContractSerializer();
        return ser.Deserialize(stream) as MyType;
    }
    finally
    {
        AppDomain.CurrentDomain.AssemblyResolve -= reh;
    }
}

Титульное "странное поведение" можно увидеть отладкой через обработчик. В то время как tryGet никогда null (требуемая сборка, в этом случае, всегда загружается в домен приложений), операция всегда завершается неудачей, если оставить ее себе. Другими словами, звоня Type.GetType(e.Name).Assembly приведет к возникновению исключения FileLoadException. Редактировать: я связывал строгое имя сборки с именем, определенным сборкой, типа; пожалуйста, игнорируйте эту ошибку. По иронии судьбы, это не выдает другую ошибку, поэтому я не уловил это, прежде чем задать этот вопрос.

Еще немного информации: Assembly.Load(e.Name) всегда возвращает действительную сборку. Я не уверен, почему это работает, тогда как метод, используемый за кулисами во время десериализации, терпит неудачу.

Fusion Log сообщает, что загрузчик пытается загрузить правильную сборку, но, поскольку сборка не найдена в закрытом пути исполняемого файла, происходит сбой.

Почему выполняется попытка загрузить сборку, когда сборка уже загружена в домен приложений?


Подробнее о фьюжн-каротаже...

Я захватил всю загрузку сборки до и во время вызова метода, который вызывает исключение. Вот соответствующие журналы, в порядке создания:

  • Частичное связывание не удалось
    • Попытка загрузить сборку только по имени
    • Только зонд базы приложения
  • Частичное связывание через LoadFrom SUCCEEDED
    • Где-ссылка привязать. Расположение указывает на то, где ссылка на файл
    • Я считаю, что VS использует LoadFrom при загрузке ссылок в решении
  • Обязательное строгое имя не удалось
    • Неизвестно, что вызвало эту попытку загрузки
    • Только зонд базы приложения

AFAICT, Visual Studio загружает сборку в домен приложения решения (если бы Fusion Log захватил домен приложения, на котором была предпринята попытка загрузки; в конце концов, он записывает вызывающую сборку).

После этого я делаю вызов десериализации. Результатом является единый вход в Fusion:

Результат привязки: hr = 0x80070002. Система не может найти указанный файл.

И снова Fusion пытается загрузить базу приложения исполняемого файла по его строгому имени. Одна хорошая вещь; он пытается загрузить из GAC, поэтому после развертывания у меня может не быть той же проблемы. Но я все еще не понимаю, почему сборка не может быть расположена в домене приложения.


Дальше интересные вещи...

Это бросает вызов Deserialize:

MyType test = new MyType ();
var serialized = Serializer.ToXml(test);
// the following line fails with a FileLoadException
var deserialized = Serializer.FromXml<MyType>(serialized);

где ToXml и FromXml оба используют NetDataContractSerializer и Write/ReadObject. Сборка загружается на раннем этапе выполнения из каталога установки пакета, но NDCS по какой-то причине не хочет использовать сборку, как она обнаружена в AppDomain. Этот тест показывает, что не может быть проблем с управлением версиями.

1 ответ

Одна вещь, которую я нахожу странным в вашем коде, это то, что здесь:

GetAssemblies().Where(x => x.FullName == e.Name)

e используется в качестве имени сборки, так как она будет соответствовать Assembly.Name, поэтому не будет иметь имя класса / типа, то здесь:

return Type.GetType(e.Name).Assembly;

e используется как полностью определенное имя типа сборки, которое, я думаю, будет содержать как имя класса / типа, так и имя сборки.

Это намеренно?


Редактировать:

Извините, я опубликовал этот ответ так же, как вы отредактировали свое сообщение и сами уловили ошибку

Другие вопросы по тегам