Загрузить не ссылающиеся Dll - PullRequest
1 голос
/ 28 июня 2019

Я пытаюсь загрузить System.Data.SqlClient динамически.

System.Data.SqlClient Nuget установлен, но в проекте нет ссылки на него.

И я не знаю правильный путь к каталогу nuget.

Есть ли способ сделать это динамически?

Вот мой код

internal static Type GetFastType(this string typeName, string assembly)
{
    if (string.IsNullOrEmpty(assembly))
        throw new Exception("AssemblyName cannot be empty");

    if (!assembly.ToLower().EndsWith(".dll"))
        assembly += ".dll";
    var key = typeName + assembly;
    if (CachedStringTypes.ContainsKey(key))
        return CachedStringTypes.Get(key);

    // Assembly.LoadFrom(assembly) // throw exception as the dll is not found
    if (!CachedAssembly.ContainsKey(assembly))
        CachedAssembly.Add(assembly, Assembly.LoadFrom(assembly));

    return CachedStringTypes.GetOrAdd(key, CachedAssembly.Get(assembly).GetType(typeName, true, true));
}

А вот как я это запускаю

var type ="System.Data.SqlClient.SqlConnection".GetFastType("System.Data.SqlClient");

Ответы [ 3 ]

1 голос
/ 28 июня 2019

Обязательное чтение:

Прочтите эту статью MSDN: Рекомендации по загрузке сборки

Короче говоря:

Похоже, вы предполагаете, что класс System.Data.SqlClient.SqlConnection всегда существует внутри System.Data.SqlClient.dll.

Это неверное предположение:

  • Пакет NuGet не является сборкой .NET.
  • Пакет NuGet не сопоставляет 1: 1 со сборкой .NET и пространствами имен.
  • Пакет NuGet может содержать несколько сборок.
  • Пакет NuGet может содержать ноль сборок.
  • Пакет NuGet может содержать сборки, в которых вообще не определены типы!
    • Это могут быть сборки, содержащие только Ресурсы или другие встроенные элементы
    • Это могут быть сборки, использующие перенаправление типов для перенаправления типов, ранее существовавших в этой сборке, на другие сборки. Только JIT использует эту функцию, однако, не отражение.
      • И эти «перенаправленные» сборки не обязательно должны существовать в пакетах NuGet: они могут быть «встроенными» сборками, встроенными в среду выполнения, например mscorlib.dll и System.Data.dll).
    • Они могут быть сборками-заглушками, которые не предоставляют никаких типов, если эти типы уже предоставлены библиотекой базовых классов - пакет NuGet существует только для предоставления этих типов для других платформ.
      • Это ситуация, с которой вы имеете дело.
  • Пакет NuGet может иметь очень разные эффекты в зависимости от цели проекта (.NET Framework, .NET Standard, .NET Core и т. Д.)

Ваш код не может предполагать, что определенный класс находится в определенном файле сборки - это нарушает представление .NET о обратной совместимости посредством пересылки типов.

В вашем случае ...

В вашем случае ваш код предполагает, что System.Data.SqlClient.SqlConnection существует внутри файла сборки с именем System.Data.SqlClient. Это предположение ложно во многих случаях, но верно в некоторых случаях.

Вот структура каталогов верхнего уровня пакета System.Data.SqlClient NuGet:

enter image description here

Обратите внимание, как внутри пакета есть подкаталоги для каждой поддерживаемой цели (в данном случае MonoAndroid10, MonoTouch10, net46, net451, net461, netcoreapp2.1, netstandard1.2 и т. Д.). Для каждой из этих целей пакет предоставляет различных сборок :

  • При нацеливании на .NET Framework 4.5.1, .NET Framework 4.6 или .NET Framework 4.6.1 будут использоваться файлы из каталогов net451, net46 и net461 (соответственно). Эти папки содержат один файл с именем System.Data.SqlClient.dll, который не содержит классов . Это связано с тем, что при нацеливании на .NET Framework 4.x типы System.Data.SqlClient (пространство имен) уже предоставляются библиотекой базовых классов внутри System.Data.dll, поэтому никаких дополнительных типов не требуется. (Так что, если вы создаете только для .NET Framework 4.x, вам не нужен пакет NuGet System.Data.SqlClient.

    Вот скриншот внутренней части этого файла сборки с помощью инструмента .NET Reflector (инструмента, который позволяет вам просматривать и декомпилировать сборки .NET), если вы мне не верите:

    enter image description here

  • При нацеливании на другие платформы через .NET Standard (то есть, когда System.Data.dll не включен по умолчанию или когда System.Data.dll не включает SqlClient), тогда пакет NuGet будет использовать netstandard1.2, Каталоги netstandard1.3, netstandard2.0, которые содержат , содержат System.Data.SqlClient.dll, а содержит - пространство имен System.Data.SqlClient с типами, которые вы ищете. Вот скриншот этой сборки:

    enter image description here

  • И другие платформы, такие как MonoAndroid, MonoTouch, xamarinios, xamarintvos и т. Д., Также имеют свою собственную версию файла сборки (или файлов!).

Но даже если вы знаете, что ваша программа будет работать только на одной конкретной платформе, где конкретный пакет NuGet содержит DLL-сборку, содержащую определенный тип, - это все равно «неправильно» из-за переадресации: https://docs.microsoft.com/en-us/dotnet/framework/app-domains/type-forwarding-in-the-common-language-runtime

Хотя переадресация типов означает, что большинство программ, которые ссылаются на типы в определенных сборках, будут продолжать работать нормально, это не относится к загрузке сборок и загрузке типов на основе отражений, что и делает ваш код. Рассмотрим этот сценарий:

  • Выпущена новая версия System.Data.SqlClient пакета NuGet, который теперь имеет две сборки:
    • System.Data.SqlClient.dll (то же, что и раньше, за исключением того, что SqlConnection удалено, но для него установлен атрибут [TypeForwardedTo], который цитирует System.Data.SqlClient.SqlConnection.dll).
    • System.Data.SqlClient.SqlConnection.dll (класс SqlConnection теперь живет в этой сборке).
  • Ваш код теперь будет ломаться, потому что он явно загружает только System.Data.SqlClient.dll, а не System.Data.SqlClient.SqlConnection.dll и перечисляет эти типы.

Здесь будут драконы ...

Теперь, если вы готовы проигнорировать все эти рекомендации и написать программы, которые предполагают, что в конкретной сборке существует определенный тип, тогда процесс прост:

// Persistent state:
Dictionary<String,Assembly> loadedAssemblies = new Dictionary<String,Assembly>();
Dictionary<(String assembly, String typeName),Type> typesByAssemblyAndName = new Dictionary<(String assembly, String typeName),Type>();

// The function:
static Type GetExpectedTypeFromAssemblyFile( String assemblyFileName, String typeName )
{
    var t = ( assemblyFileName, typeName );
    if( !typesByName.TryGetValue( t, out Type type ) )
    {
        if( !loadedAssemblies.TryGetValue( assemblyFileName, out Assembly assembly ) )
        {
            assembly = Assembly.LoadFrom( assemblyFileName );
            loadedAssemblies[ assemblyFileName ] = assembly;
        }
        type = assembly.GetType( typeName ); // throws if the type doesn't exist
        typesByName[ t ] = type;
    }
    return type;
}

// Usage:

static IDbConnection CreateSqlConnection()
{
    const String typeName         = "System.Data.SqlClient.SqlConnection";
    const String assemblyFileName = "System.Data.SqlClient.dll";

    Type sqlConnectionType = GetExpectedTypeFromAssemblyFile( assemblyFileName, typeName );

    Object sqlConnectionInstance = Activator.CreateInstance( sqlConnectionType ); // Creates an instance of the specified type using that type's default constructor.
    return (IDbConnection)sqlConnectionInstance;
}
0 голосов
/ 29 июня 2019

Для тех, у кого может быть та же проблема, я нашел решение

Вот как можно правильно загрузить нужный тип

 var type = Type.GetType($"{typeName}, {assembly}");
 eg.
 var type =Type.GetType("System.Data.SqlClient.SqlConnection, System.Data.SqlClient");

Таким образом, оно должнозагрузить dll динамически.

0 голосов
/ 28 июня 2019

Я думаю, вы должны указать полный путь к LoadFrom(...).Вы должны знать путь поиска приложения, поэтому просто сопоставьте этот путь с именем сборки.Я не думаю, что просто загрузить с пути, который не находится на пути поиска, если только не делать некоторые трюки с доменом приложения.

...