Суть вопроса
В большинстве случаев .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=".x86Oracle.DataAccess.dll"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Oracle.DataAccess"
publicKeyToken="89b483f429c47342"
culture="neutral"
processorArchitecture="amd64" />
<codeBase
version="4.112.2.0"
href=".x64Oracle.DataAccess.dll"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
У атрибута processorArchitecture
четыре возможных значения: x86
, amd64
, msil
, ia64
. Пути в codeBase
могут отличаться в зависимости от типа проекта (например, для ASP.NET должно быть href=".binx64Oracle.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)..externalsOracleICx86*.dll">
<Visible>False</Visible>
</OracleICFilesx86>
<OracleICFilesx64
Include="$(MSBuildProjectDirectory)..externalsOracleICx64*.dll">
<Visible>False</Visible>
</OracleICFilesx64>
<OdpNetFilesx86
Include="$(MSBuildProjectDirectory)..externalsOdp.Netx86*.dll">
<Visible>False</Visible>
</OdpNetFilesx86>
<OdpNetFilesx64
Include="$(MSBuildProjectDirectory)..externalsOdp.Netx64*.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>
Автор: vlio