Выбор платформозависимой библиотеки в runtime

от автора

Суть вопроса

В большинстве случаев .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>

ссылка на оригинал статьи http://habrahabr.ru/post/165767/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *