Arhn - архитектура программирования

Как загрузить сборки, расположенные в папке в консольном приложении .NET Core

Я делаю консольное приложение на платформе .NET Core, и мне было интересно, как можно загружать сборки (файлы .dll) и создавать экземпляры классов с использованием динамических функций C #? Кажется, что это так сильно отличается от .NET 4.X, и это не совсем документировано ...

Например, скажем, у меня есть библиотека классов (.NET Core), и в ней только один класс:

namespace MyClassLib.SampleClasses
{
    public class Sample
    {
        public string SayHello(string name)
        {
            return $"Hello {name}";
        }

        public DateTime SayDateTime()
        {
            return DateTime.Now;
        }
    }
}

Таким образом, имя файла dll будет MyClassLib.dll, и он находится в /dlls/MyClassLib.dll.

Теперь я хочу загрузить это в простое консольное приложение (.NET Core), создать экземпляр класса Sample и вызвать методы с использованием динамических функций C # в следующем консольном приложении:

namespace AssemblyLoadingDynamic
{
    public class Program
    {
        public static void Main(string[] args)
        {
            // load the assembly and use the classes
        }
    }
}

Примечание. Под .NET Core я имею в виду версию RC2.


Ответы:


1

В настоящее время вы работаете с netcoreapp1.0, вам не нужно вдаваться в подробности реализации своего собственного AssemblyLoader. Существует Default, который отлично работает. (Следовательно, @ VSG24 упоминает, что Load ничего не делает).

using System;
using System.Runtime.Loader;

namespace AssemblyLoadingDynamic
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var myAssembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(@"C:\MyDirectory\bin\Custom.Thing.dll");
            var myType = myAssembly.GetType("Custom.Thing.SampleClass");
            var myInstance = Activator.CreateInstance(myType);
        }
    }   
}

где project.json выглядит так:

{
  "version": "1.0.0-*",
  "buildOptions": {
    "emitEntryPoint": true
  },

  "dependencies": {
    "Microsoft.NETCore.App": {
      "type": "platform",
      "version": "1.0.1"
    },
    "System.Runtime.Loader": "4.0.0"
  },

  "frameworks": {
    "netcoreapp1.0": {
      "imports": "dnxcore50"
    }
  }
}
10.11.2016
  • Это ооочень лучше, чем предыдущие ответы. Спасибо! 29.11.2016
  • Не загружает сборки .NET Core, на удивление загружает только обычные сборки .NET Framework !!! 02.07.2017

  • 2

    Не уверен, что это лучший способ сделать это, но вот что я придумал:

    (Проверено только на .Net Core RC2 - Windows)

    using System;
    using System.Linq;
    using System.Reflection;
    using System.Runtime.Loader;
    using Microsoft.Extensions.DependencyModel;
    
    namespace AssemblyLoadingDynamic
    {
        public class Program
        {
            public static void Main(string[] args)
            {
                var asl = new AssemblyLoader();
                var asm = asl.LoadFromAssemblyPath(@"C:\Location\Of\" + "SampleClassLib.dll");
    
                var type = asm.GetType("MyClassLib.SampleClasses.Sample");
                dynamic obj = Activator.CreateInstance(type);
                Console.WriteLine(obj.SayHello("John Doe"));
            }
    
            public class AssemblyLoader : AssemblyLoadContext
            {
                // Not exactly sure about this
                protected override Assembly Load(AssemblyName assemblyName)
                {
                    var deps = DependencyContext.Default;
                    var res = deps.CompileLibraries.Where(d => d.Name.Contains(assemblyName.Name)).ToList();
                    var assembly = Assembly.Load(new AssemblyName(res.First().Name));
                    return assembly;
                }
            }
        }
    }
    

    MyClassLib.SampleClasses - это пространство имен, а Sample - это тип, также известный как имя класса.

    При выполнении он попытается загрузить SampleClassLib.dll скомпилированную библиотеку классов в память и предоставит моему консольному приложению доступ к MyClassLib.SampleClasses.Sample (взгляните на вопрос), затем мое приложение вызывает метод SayHello() и передает ему имя «John Doe», Поэтому программа печатает:

    "Hello John Doe"

    Краткое примечание: переопределение для метода Load не имеет значения, поэтому вы можете просто заменить его содержимое на throw new NotImplementedException(), и это не должно повлиять ни на что, о чем мы заботимся.

    18.06.2016
  • AssemblyLoadContext недоступен в asp.net core 1.0, его можно найти только в System.Runtime.Loader 4.0.0-beta-23516 27.09.2016
  • @DilyanDimitrov Я без проблем использую приведенный выше код в версии 1.0. 27.09.2016
  • В каком пакете вы можете найти этот класс AssemblyLoadContext, мой VS предлагает добавить пакет, о котором я упоминал выше 27.09.2016
  • @DilyanDimitrov Вот весь абстрактный класс gist.github.com/VSG24/9fe807a1f96073e если вы просто хотите загрузить какую-то сборку, просто верните null в методе Load 27.09.2016

  • 3

    Спасибо, что поделились. Он также работает с Net Core 1.0. Если вашей сборке требуются другие сборки по тому же пути, вы можете использовать приведенный ниже пример кода.

    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Runtime.Loader;
    using Microsoft.Extensions.DependencyModel;
    public class AssemblyLoader : AssemblyLoadContext
    {
        private string folderPath;
    
        public AssemblyLoader(string folderPath)
        {
            this.folderPath = folderPath;
        }
    
        protected override Assembly Load(AssemblyName assemblyName)
        {
            var deps = DependencyContext.Default;
            var res = deps.CompileLibraries.Where(d => d.Name.Contains(assemblyName.Name)).ToList();
            if (res.Count > 0)
            {
                return Assembly.Load(new AssemblyName(res.First().Name));
            }
            else
            {
                var apiApplicationFileInfo = new FileInfo($"{folderPath}{Path.DirectorySeparatorChar}{assemblyName.Name}.dll");
                if (File.Exists(apiApplicationFileInfo.FullName))
                {
                    var asl = new AssemblyLoader(apiApplicationFileInfo.DirectoryName);
                    return asl.LoadFromAssemblyPath(apiApplicationFileInfo.FullName);
                }
            }
            return Assembly.Load(assemblyName);
        }
    }
    

    Не забудьте добавить в ваш project.json файл следующие зависимости:

     "System.Runtime.Loader"
     "Microsoft.Extensions.DependencyModel"
    
    09.08.2016
  • Сработал для меня 02.09.2016
  • А если ему нужна другая сборка, которая находится в каталогах пакетов .NET? 23.09.2016
  • Это работает для загрузки сборки, но не кажется полностью правильным, проверка IsSubclassOf базовых типов во вновь загруженных типах возвращает false для известных унаследованных типов. 16.07.2017

  • 4

    Используя .NET Core 1.1 / Standard 1.6, я обнаружил, что AssemblyLoader недоступен, и AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath) выдал мне ошибку «Не удалось загрузить файл или сборку xxx».

    Наконец, это решение, приведенное ниже, сработало для меня - просто путем добавления шага для получения объекта AssemblyName:

    var assemblyName = AssemblyLoadContext.GetAssemblyName(assemblyPath);
    var assembly = Assembly.Load(assemblyName);
    
    26.07.2017
  • Спасибо! К сожалению, у меня также есть AssemblyLoadContext.Default.Resolving, который также вызывает исключение. 20.09.2017
  • Первое решение теперь работает в .NET Core 2.x и новее. 05.11.2019

  • 5

    @Rob, единственный способ создать ваш пример - это изменить тип myInstance на динамический.

    Если оставить тип как var, код будет построен, но как только я попытаюсь использовать метод из загруженной сборки во время выполнения, я получаю ошибки компилятора, такие как myInstance не содержит метода X < / em>. Я новичок в этом, но пометить тип как динамический, кажется, имеет смысл. Если тип загружается во время выполнения, как компилятор может проверить, что myInstance будет содержать метод X или опору Y? Вводя myInstance как dynamic, я считаю, что вы удаляете проверку компилятора, и, таким образом, я мог бы получить пример для сборки и запуска без проблем. Не уверен, что это на 100% правильный способ (я недостаточно знаю, и вы можете посоветовать, что есть проблема с использованием динамического?), Но это единственный способ заставить его работать, не создавая свои собственные AssemblyLoader (как вы правильно заметили).

    So...

    using System;
    using System.Runtime.Loader;
    
    namespace TestApp
    {
        class Program
        {
            static void Main(string[] args)
            {
                var myAssembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(@"C:\Documents\Visual Studio 2017\Projects\Foo\Foo\bin\Debug\netcoreapp2.0\Foo.dll");
                var myType = myAssembly.GetType("Foo.FooClass");
                dynamic myInstance = Activator.CreateInstance(myType);
                myInstance.UpperName("test");
            }
        }
    }
    

    Надеюсь, это поможет кому-то, так как я был новичком, мне потребовалось время, чтобы определить, почему myInstance как var не имеет метода X и т. Д. Doh!

    20.12.2017
  • Это правильно. Вывод типа обычно не работает при работе с динамическими типами. 19.04.2018

  • 6

    Я много копаюсь в этом, я пробовал подход DependencyContext ... Он работает хорошо, но имеет некоторые ограничения и отличается от стандартного разрешения сборки, которое есть в приложении dotnet c ++, которое запускает ваше приложение. Вам нужно выполнить сопоставление имен вручную, и если ваше хост-приложение является опубликованным, у вас не будет пути исследования для папки nuget, что является проблемой (решаемой), если ваша дочерняя сборка находится в отладке и использует nuget ...

    Итак, вот еще одно решение: если приложение (assemblyA), вручную загружающее сборку (assemblyB), не имеет зависимостей (или нет конфликтующих зависимостей с assemblyB), я предлагаю обмануть и использовать по умолчанию разрешение сборки AssemblyB. Для dotnet.exe есть скрытый драгоценный камень, который позволяет вам загрузить выбранный вами файл deps, чтобы вы могли сделать что-то вроде этого:

    dotnet exec --depsfile pathToAssemblyB\assemblyB.deps.json --runtimeconfig pathToAssemblyB\assemblyB.runtimeconfig.json AssemblyA.dll
    

    а затем вы можете загрузить сборку, как описано в других ответах, с помощью

    var myAssembly = AssemblyLoadContext.Default.LoadFromAssemblyPath("pathToAssemblyB\\AssemblyB.dll");

    Таким образом, он правильно разрешит все зависимости для сборки B, но не для сборки A. Это обратная проблема, но если у вас есть небольшое приложение, которое хочет выполнять удаленное взаимодействие в большом приложении, это полезно. Другая проблема заключается в том, что вам нужно знать, что вы собираетесь использовать сборку AssemblyB при запуске приложения и что она работает только один раз за выполнение. Итак, существует другой набор проблем, и вы можете выбрать свой подход в зависимости от ситуации. Обратите внимание, что это неподдерживаемая / недокументированная функция, но она используется в основных инструментах EF, поэтому на данный момент она «жизнеспособна» ...

    13.11.2018

    7

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

        /// <summary>
        /// Gets the assemblies that belong to the application .exe subfolder.
        /// </summary>
        /// <returns>A list of assemblies.</returns>
        private static IEnumerable<Assembly> GetAssemblies()
        {
            string executableLocation = AppContext.BaseDirectory;
            string directoryToSearch = Path.Combine(Path.GetDirectoryName(executableLocation), "Plugins");
            foreach (string file in Directory.EnumerateFiles(directoryToSearch, "*.dll"))
            {
                Assembly assembly = null;
                try
                {
                    //Load assembly using byte array
                    byte[] rawAssembly = File.ReadAllBytes(file);
                    assembly = Assembly.Load(rawAssembly);
                }
                catch (Exception)
                {
                }
    
                if (assembly != null)
                {
                    yield return assembly;
                }
            }
        }
    

    еще один, но в .netstandard1.3 ни того, ни другого не было.

    var assembiles = Directory.GetFiles(Assembly.GetEntryAssembly().Location, "*.dll", SearchOption.TopDirectoryOnly)
            .Select(AssemblyLoadContext.Default.LoadFromAssemblyPath);
    
    09.10.2019

    8

    Я использовал следующий код для загрузки сборки и вызова метода внутри класса из загруженной сборки.

        private static FormCustomized loadLayout(global::System.String layoutFilename, global::System.String layoutNameSpace)
        {
            FormCustomized mainForm = default;
            Type typeMainLayout = default;
            FileInfo layoutFile;
            layoutFile = new FileInfo(layoutFilename);
            layoutFile.Refresh();
            if (!layoutFile.Exists)
            {
                MessageBox.Show("Layout file not found. You need to reinstall the program");
                return default;
            }
    
            try
            {
                Assembly assemblyRaw = Assembly.LoadFrom(layoutFilename);
                AssemblyLoadContext context = AssemblyLoadContext.Default;
                Assembly assembly = context.LoadFromAssemblyPath(layoutFilename);
    
    
                Type typeMainLayoutIni = assembly.GetType(layoutNameSpace + ".initializeLayoutClass");
                Object iniClass = Activator.CreateInstance(typeMainLayoutIni, true);
                MethodInfo methodInfo = typeMainLayoutIni.GetMethod("AssembliesToLoadAtStart");
                enVars.assemblies = (Dictionary<string, Environment.environmentAssembliesClass>)methodInfo.Invoke(iniClass, default);
                typeMainLayout = assembly.GetType(layoutNameSpace + ".mainAppLayoutForm");
                mainForm = Activator.CreateInstance(typeMainLayout, enVars) as FormCustomized;
            }
            catch (Exception ex)
            {
                return default;
            }
    
            return default;
        }
    
    12.08.2020
    Новые материалы

    Коллекции публикаций по глубокому обучению
    Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

    Представляем: Pepita
    Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

    Советы по коду Laravel #2
    1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

    Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
    Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

    3 способа решить квадратное уравнение (3-й мой любимый) -
    1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

    Создание VR-миров с A-Frame
    Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

    Демистификация рекурсии
    КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..


    © 2024 arhn.ru, Arhn - архитектура программирования