Можно ли заменить ссылку на сборку со строгим именем на «слабую» ссылку? - PullRequest
17 голосов
/ 29 июля 2011

Я пишу инструмент .NET, для которого требуется SMO-библиотека SQL Server.Мне все равно, если это версия с Server 2005 (9.0), 2008 (10.0) или 2008 R2 (вероятно, 10.5, не проверял).Библиотека SMO устанавливается вместе с SQL Server, поэтому я могу с уверенностью предположить, что в любой системе с установленным SQL Server также доступна некоторая версия библиотеки SMO.

К сожалению, библиотеки SMO ​​имеют строгие имена:Если я добавлю ссылку на SMO 9.0 в моем проекте, она не будет выполнена (FileNotFoundException), если в системе клиента присутствует только SMO 10.0, и наоборот.

Есть ли способ сказать,компилятор, который любая версия библиотеки мне подходит? Или мне действительно нужно распространять 3 идентичные версии моего инструмента, каждая из которых скомпилирована в свою версию SMO?


Отказ от ответственности: я знаю, что библиотеки SMO ​​(и библиотеки, необходимые для библиотек SMO) можно распространять.Но есть большая разница между (а) одним компактным 100 КБ автономным EXE-файлом и (б) полнофункциональным установочным пакетом, который устанавливает целую кучу предварительных условий.

Отказ от ответственности 2: Я в курсеиз следующих дубликатов:

Однако предоставленные решения не подходят.В вопросе 1 разработчик контролирует библиотеку DLL, на которую ссылаются (а я нет);в вопросе 2 разработчик имеет контроль над целевыми системами (чего я тоже не имею).

Ответы [ 3 ]

6 голосов
/ 29 июля 2011

Как я знаю, невозможно удалить зависимость от точной версии.Это одна из причин, почему существуют строгие имена - чтобы избежать несоответствия версий.Внутренние или даже общедоступные интерфейсы сборки могут изменяться в зависимости от версии, и вы можете обнаружить, что новая версия не обратно совместима со старой.Из-за этого .NET ищет версию, используемую во время компиляции, чтобы убедиться, что приложение работает правильно.

Если третья сторона решит, что ее новая версия обратно совместима, и если они развернут сборку в GAC, они могут добавить издательполитика , которая будет выполнять перенаправление автоматически.

Если вы решите, что хотите принудительно загрузить другую сборку, вы можете использовать подход, упомянутый @chibacity, или обработчик реализации для AppDomain.CurrentDomain.AssemblyResolve.Это событие возникает, когда .NET не может найти ссылочную сборку, и вы можете реализовать свою собственную логику, чтобы найти ее и загрузить, вызвав Assembly.LoadFrom.В таком случае вам решать, какую версию вы загружаете.

6 голосов
/ 29 июля 2011

Можно использовать Перенаправление привязки сборки .

Например:

 <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Telerik.Web.UI" publicKeyToken="121fae78165ba" />
        <bindingRedirect
               oldVersion="2010.0.0.1"
               newVersion="2011.1.315.40" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>

Обновление

Я вижу из вашего комментария, что мы должны немного подумать об этом в обратном порядке.

Очень многообещающий подход, но, к сожалению, он просто заменяет зависимость от версии X (сильную) зависимость от версии Y. У меня все еще есть зависимость от одной конкретной версии.

Я провел несколько экспериментов, где скомпилировал версию сборки: 4.0.0.0, но хотел убедиться, что она загрузит эту версию, а также некоторые выбранные более старые версии. Таким образом, вы не зависите от какой-либо одной версии, но от любой из настроенных вами версий.

Следующее обеспечит загрузку VersionedAssembly, если в системе будет установлена ​​любая из следующих версий: 4.0.0.0, 3.0.0.0, 2.0.0.0, 1.0.0.0.

   <runtime>
      <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
         <dependentAssembly>
            <assemblyIdentity name="VersionedAssembly" publicKeyToken="20d85e" />
            <bindingRedirect oldVersion="4.0.0.0" newVersion="1.0.0.0"/>
         </dependentAssembly>
         <dependentAssembly>
            <assemblyIdentity name="VersionedAssembly" publicKeyToken="20d84e" />
            <bindingRedirect oldVersion="4.0.0.0" newVersion="2.0.0.0"/>
         </dependentAssembly>
         <dependentAssembly>
            <assemblyIdentity name="VersionedAssembly" publicKeyToken="20d84e" />
            <bindingRedirect oldVersion="4.0.0.0" newVersion="3.0.0.0"/>
         </dependentAssembly>
      </assemblyBinding>
   </runtime>
0 голосов
/ 17 октября 2018

Основываясь на предложении Ладислава о переопределении AssemblyResolve, я смог найти следующее решение:

Sub Main()
    ...
    Dim assembly = GetSmoAssembly()
    If assembly Is Nothing Then
        ' no suitable Version of SMO found
        ...
    Else
        ' load correct assembly
        Dim returnAssembly As ResolveEventHandler = Function() assembly
        AddHandler AppDomain.CurrentDomain.AssemblyResolve, returnAssembly
        TestSmo()
        RemoveHandler AppDomain.CurrentDomain.AssemblyResolve, returnAssembly
    End If
    ...
End Sub

Private Function GetSmoAssembly() As Assembly
    Try
        Return Assembly.Load("Microsoft.SqlServer.Smo, Version=9.0.242.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91")
    Catch ex As FileNotFoundException
    End Try

    Try
        Return Assembly.Load("Microsoft.SqlServer.Smo, Version=10.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91")
    Catch ex As FileNotFoundException
    End Try

    Return Nothing
End Function

' Needs to be in a separate method, see https://stackoverflow.com/q/6847765/87698
Private Sub TestSmo()
    Dim srv As New Smo.Server()
End Sub

Примечание. Использование Assembly.Load непосредственно в событии AssemblyResolveОбработчик не очень хорошая идея, так как он рекурсивно вызывает обработчик событий, если загрузка не удалась.

...