Суть вопроса
В большинстве случаев .NET-приложения являются платформонезависимыми. Мы ожидаем, что наше приложение будет одинаково выполняться как в 32-хразрядной ОС, так и 64-хразрядной.
Так обычно и происходит до тех пор, пока нам не понадобится использовать внешние платформозависимые библиотеки, например неуправляемые. Если такая библиотека существует в вариантах и для
x86
, и для x64
, то это может принести нам определенную головную боль. Будем исходить из того, что ограничивать наше приложение, например, только 32-хразрядным процессом не в наших правилах.Возможно, нам придется поддерживать вдвое больше конфигураций проекта. В этом случае при отладке придется переключать конфигурации, ведь разработческий веб-сервер Cassini существует только в
x86
варианте, а ReSharper может запускать тесты и в 64-хразрядном процессе. Кроме того, придется выпускать два дистрибутива и предоставлять пользователю при скачивании с сайта ох какой нелегкий выбор. Поэтому разумным решением выглядит выбор подходящей для работы библиотеки уже в runtime в зависимости от того, в каком процессе (32-х или 64-хразрядном) код выполняется. При этом сами проекты остаются AnyCPU
.В нашем приложении необходимо подключаться к к Oracle Database, для чего используются библиотеки Oracle Instant Client и Oracle Data Provider for .NET.
Решение
Решение было найдено в виде тега
runtime/assemblyBinding
конфигурационного файла приложения. В app.config
добавляем следующее:<configuration>
<!-- Выбор версии библиотеки ODP.NET в зависимости от Runtime архитектуры -->
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Oracle.DataAccess"
publicKeyToken="89b483f429c47342"
culture="neutral"
processorArchitecture="x86" />
<codeBase
version="4.112.2.0"
href=".\x86\Oracle.DataAccess.dll"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Oracle.DataAccess"
publicKeyToken="89b483f429c47342"
culture="neutral"
processorArchitecture="amd64" />
<codeBase
version="4.112.2.0"
href=".\x64\Oracle.DataAccess.dll"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
У атрибута
processorArchitecture
четыре возможных значения: x86
, amd64
, msil
, ia64
. Пути в codeBase
могут отличаться в зависимости от типа проекта (например, для ASP.NET должно быть href=".\bin\x64\Oracle.DataAccess.dll"
).Ну а для того чтобы библиотеки оказались в нужных папках, в файлы «исполняемых» проектов (тестовые сборки, веб-сервисы и сайты, и истинно исполняемые приложения
.exe
) после строки<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
добавляется включение собственных целей MSBuild:
<Import Project="$(MSBuildProjectDirectory)\..\CommonItems.targets" />
В файле проекта обычно есть закомментированная цель
AfterBuild
. Ее необходимо раскомментировать/добавить/отредактировать: <Target Name="AfterBuild" DependsOnTargets="CopyDataAccessFiles" >
Файл
CommonItems.targets
содержит описание этих общих элементов для исполняемых проектов. Здесь определена цель по копированию зависимостей:<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<OracleICFilesx86
Include="$(MSBuildProjectDirectory)\..\externals\OracleIC\x86\*.dll">
<Visible>False</Visible>
</OracleICFilesx86>
<OracleICFilesx64
Include="$(MSBuildProjectDirectory)\..\externals\OracleIC\x64\*.dll">
<Visible>False</Visible>
</OracleICFilesx64>
<OdpNetFilesx86
Include="$(MSBuildProjectDirectory)\..\externals\Odp.Net\x86\*.dll">
<Visible>False</Visible>
</OdpNetFilesx86>
<OdpNetFilesx64
Include="$(MSBuildProjectDirectory)\..\externals\Odp.Net\x64\*.dll">
<Visible>False</Visible>
</OdpNetFilesx64>
</ItemGroup>
<Target Name="CopyDataAccessFiles" >
<Copy
SourceFiles="@(OracleICFilesX86);@(OdpNetFilesx86)"
DestinationFolder="$(MSBuildProjectDirectory)\$(OutputPath)\x86\"
SkipUnchangedFiles="true"
UseHardLinkIfPossible="true" />
<Copy
SourceFiles="@(OracleICFilesX64);@(OdpNetFilesx64)"
DestinationFolder="$(MSBuildProjectDirectory)\$(OutputPath)\x64\"
SkipUnchangedFiles="true"
UseHardLinkIfPossible="true" />
</Target>
</Project>
Ограничения
У предложенного способа есть ограничения:
- Предполагается, что мы не используем типы управляемой платформозависимой библиотеки и, таким образом, не нуждаемся в ссылке на сборку (Oracle.DataAccess.dll) в своем проекте. Т.е. от сборки зависимость есть, но неявная, динамическая.
- Теперь мы с собой в дистрибутиве «тащим» библиотеки для всех поддерживаемых платформ. Для OracleIC это более 100 МБ на платформу.
Ссылки
<assemblyBinding> Element for <runtime>