Использование нескольких версий одной и той же библиотеки DLL
Мне было поручено создать новый модуль для приложения, и поэтому я добавляю в проект новые библиотеки DLL. Все хорошо и хорошо.
Однако в моих DLL я бы хотел использовать новую версию внешней DLL (над которой я не контролирую). Если я просто ссылаюсь на новую DLL и работаю только с этим, мой код будет работать, но старый код перестанет работать.
Could not load file or assembly 'itextsharp, Version=5.0.6.0, Culture=neutral,
PublicKeyToken=8354ae6d2174ddca' or one of its dependencies. The located assembly's
manifest definition does not match the assembly reference. (Exception from HRESULT:
0x80131040)
Я пробовал простой трюк по изменению имени DLL, но, по-видимому, это было слишком наивно, полагая, что это сработает. Я пытался использовать внешние псевдонимы (определяя их в своих ссылках), но я до сих пор не знаю, как получить два файла с тем же именем в одной папке BIN...
Что мне делать?
Ответы
Ответ 1
Вы можете загрузить другую версию в конкретный AppDomain
Возможно, слишком подробно, но вот статья, которая демонстрирует использование AppDomains в полезной настройке и как они работают:
http://msdn.microsoft.com/en-us/magazine/cc164072.aspx
В самом основном смысле это сводится к этому образцу кода:
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
...
static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
if (/*some condition*/)
return Assembly.LoadFrom("DifferentDllFolder\\differentVersion.dll");
else
return Assembly.LoadFrom("");
}
Ответ 2
Другим жизнеспособным вариантом является использование extern
, как описано здесь:
http://blogs.msdn.com/b/abhinaba/archive/2005/11/30/498278.aspx
Ответ 3
Вы также можете полагаться на перенаправление привязки сборки для своей сильной именованной сборки, как описано в http://msdn.microsoft.com/en-us/library/2fc472t2.aspx.
У вас будет только одна версия файла (последняя), и обе ссылки разрешат его.
Ответ 4
Если решение AppDomains не применимо в вашем случае, вы находитесь под давлением времени, имеете противоречивые требования (как это когда-либо случается) и не возражаете против смехотворно выдуманных хаков:
- Декомпилируйте новую версию сборки, используя инструмент ildasm (часть командной строки Developer, включенная в Visual Studio)
- Отредактируйте сгенерированный файл .il, чтобы найти/заменить ссылки пространства имен сборки. Используя приведенный пример, это будет изменение от itextsharp.X до itextsharp.new.X
- Аналогичным образом отредактируйте значение для атрибута AssemblyTitleAttribute. Это требует перевода символов ASCII в hex.
- Скомпилируйте файл .il с помощью ilasm
- Обратите внимание, что это может потребоваться повторить для любых зависимых сборок (например,
someassembly.core.whatever)
- Добавьте новые .dll в свой проект с другим именем и укажите их явно (а не через nuget или что-то еще)
Эй, не смотри на меня так. Я действительно сказал нелепо надуманный взлом...
Ответ 5
Предположим, что у вас есть структура проекта следующим образом:
![Схема проекта]()
... где A
и B
- это библиотеки классов, а C
- это проект исполняемого типа (например, unit тест или консольный проект).
Предположим, что структура папок такова:
<Предварительно > <код > ABC.sln
А /A.csproj
A/...
Б /B.csproj
B/...
С /C.csproj
C/...
Библиотека /thirdparty 4/thirdparty.dll
Библиотека/thirdparty5/thirdparty.dll
Код > Если бы мы попытались наивно ссылаться на наши проекты вместе, у нас возникла бы проблема: две версии thirdparty.dll
будут скопированы в одну и ту же папку (каталог вывода (т.е. bin) <код > Скод > ). Нам нужен способ C
скопировать обе библиотеки dll в его выходной каталог и предоставить механизм для ссылки на один.
Чтобы решить эту проблему, я модифицировал C.csproj
, чтобы содержать следующее:
<Предварительно > <код > <ItemGroup> < Content Include = "..\lib\thirdparty4\thirdparty.dll" > <CopyToOutputDirectory> PreserveNewest </CopyToOutputDirectory> < & Ссылка GT; thirdparty4\thirdparty.dll </Ссылка > </Содержание > < Content Include = "..\lib\thirdparty5\thirdparty.dll" > <CopyToOutputDirectory> PreserveNewest </CopyToOutputDirectory> < & Ссылка GT; thirdparty5\thirdparty.dll </Ссылка > </Содержание > </ItemGroup> Код > Это даст указание создать в своем выходном каталоге как thirdparty4\thirdparty.dll
, так и thirdparty5\thirdparty.dll
.
Теперь, после создания C
, его выходной каталог выглядит следующим образом:
<Предварительно > <код > C\Bin\Debug\A.DLL
C\Bin\Debug\B.dll
C\Bin\Debug\C.dll
C\Bin\Debug\thirdparty4\thirdparty.dll
C\Bin\Debug\thirdparty5\thirdparty.dll
Код > Чтобы проинструктировать C
для использования обеих этих библиотек, я добавил к нему файл App.config
со следующим:
<? xml version = "1.0" encoding = "utf-8"? >
< & конфигурация GT; < & выполнение GT; < assemblyBinding xmlns = "urn: schemas-microsoft-com: asm.v1" > <dependentAssembly> < assemblyIdentity name= "thirdparty" culture = "neutral" publicKeyToken = "1234567890123445" /> < bindingRedirect oldVersion = "4.0.0.0-4.0.0.0" newVersion = "4.0.0.0" /> < codeBase version = "4.0.0.0" href= "thirdparty4\thirdparty.dll" /> </dependentAssembly> <dependentAssembly> < assemblyIdentity name= "thirdparty" culture = "neutral" publicKeyToken = "1234567890123445" /> < bindingRedirect oldVersion = "5.0.0.0-5.0.0.0" newVersion = "5.0.0.0" /> < codeBase version = "5.0.0.0" href= "thirdparty5\thirdparty.dll" /> </dependentAssembly> </assemblyBinding></Л;/& выполнения GT;
</конфигурации >
Код>
Это даст указание сборке, в зависимости от того, какая версия нужна, использовать одну DLL или другую, которые будут доступны в подпапках выходного каталога. (bindingRedirect являются необязательными, но вы можете использовать их, если вам нужен ряд изменений для этого.)