Странное поведение при попытке получить тип по 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
используется как полностью определенное имя типа сборки, которое, я думаю, будет содержать как имя класса / типа, так и имя сборки.
Это намеренно?
Редактировать:
Извините, я опубликовал этот ответ так же, как вы отредактировали свое сообщение и сами уловили ошибку