Создать экземпляр из сборки и его зависимости в C # - PullRequest
4 голосов
/ 22 января 2009

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

//Create a C# code provider 
CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");

//Set the complier parameters
CompilerParameters cp = new CompilerParameters();
cp.GenerateExecutable = false;
cp.GenerateInMemory = false;
cp.TreatWarningsAsErrors = false;
cp.WarningLevel = 3;
cp.OutputAssembly = "SomeOutputPathForDLL";

// Include referenced assemblies
cp.ReferencedAssemblies.Add("mscorlib.dll");
cp.ReferencedAssemblies.Add("System.dll");
cp.ReferencedAssemblies.Add("System.Core.dll");
cp.ReferencedAssemblies.Add("System.Data.dll");
cp.ReferencedAssemblies.Add("System.Data.DataSetExtensions.dll");
cp.ReferencedAssemblies.Add("System.Xml.dll");
cp.ReferencedAssemblies.Add("System.Xml.Linq.dll");
cp.ReferencedAssemblies.Add("MyApp.exe");
cp.ReferencedAssemblies.Add("SomeFolder\SomeAdditionalReferencedDLL.dll");

// Set the compiler options
cp.CompilerOptions = "/target:library /optimize";
CompilerResults cr = provider.CompileAssemblyFromFile(cp, "PathToSourceCodeFile");

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

Assembly assembly = Assembly.LoadFile("PathToNewClassDLL");
Blah newBlah = assembly.CreateInstance("MyApp.BlahNamespace.Blah") as Blah;

Когда я вызываю Assembly.CreateInstance, как я это делал выше, я получаю сообщение об ошибке, в котором говорится, что он не может создать экземпляр. Когда я проверяю assembly.GetReferencedAssemblies (), он имеет стандартные ссылки и ссылку для моего приложения (MyApp.exe), но у него нет ссылки на зависимый базовый класс, который я использовал при первоначальной компиляции класса (SomeAdditionalReferencedDLL.dll) .

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

Спасибо

Ответы [ 6 ]

3 голосов
/ 22 января 2009

Если вы вручную загрузите внешнюю DLL (сборку), она не будет автоматически загружать то, на что вы ссылаетесь.

Так что вам нужно будет создать AssemblyLoader. Код, который проверяет ссылочные сборки для вашей сборки и загружает их самостоятельно.

Если есть сложности с ссылочными сборками, которые находятся в нечетных папках на вашем компьютере и не вместе с вашей скомпилированной DLL, проверьте событие AppDomain.CurrentDomain.AssemblyResolve. (Вы используете его, чтобы обмануть .NET, чтобы принять загружаемую сборку, даже если ее нет в GAC или в вашей скомпилированной DLL) *

После того как вы вручную загрузили указанную DLL-библиотеку с кодом, CreateInstance будет работать.

0 голосов
/ 01 июня 2010
//Constructor
static MyClass()
    {
        //Provoque l'événement quand .Net ne sait pas retrouver un Assembly référencé
        AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);

    }
    /// <summary>
    /// Mémorise les assembly référencés par d'autres qui ne sont pas dans le répertoire principal d'EDV
    /// </summary>
    static Dictionary<string, string> _AssembliesPath;
    /// <summary>
    /// .Net ne sait pas retrouver un Assembly référencé
    /// Cherche et charge d'après les assembly référencés par d'autres qui ne sont pas dans le répertoire principal d'EDV        
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="args"></param>
    /// <returns></returns>
    static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
        if (_AssembliesPath != null && _AssembliesPath.ContainsKey(args.Name))
        {
            Assembly lAssembly = Assembly.LoadFile(_AssembliesPath[args.Name]);
            AddAssemblyReferencedAssemblies(lAssembly, System.IO.Path.GetDirectoryName(lAssembly.Location));
            return lAssembly;
        }
        Error = string.Format("L'assembly {0} n'a pu être chargé", args.Name);
        return null;
    }
    /// <summary>
    /// Mémorise les assembly référencés par d'autres qui ne sont pas dans le répertoire principal d'EDV        
    /// </summary>
    /// <param name="pAssembly"></param>
    /// <param name="psRootPath"></param>
    static void AddAssemblyReferencedAssemblies(Assembly pAssembly, string psRootPath)
    {
        if (_AssembliesPath == null) _AssembliesPath = new Dictionary<string, string>();
        foreach (AssemblyName lRefedAss in pAssembly.GetReferencedAssemblies())
            if (!_AssembliesPath.ContainsKey(lRefedAss.FullName))
            {
                string lsRoot = psRootPath + "\\" + lRefedAss.Name + ".";
                string lsExt;
                if (System.IO.File.Exists(lsRoot + (lsExt = "dll")) || System.IO.File.Exists(lsRoot + (lsExt = "exe")))
                {
                    _AssembliesPath.Add(lRefedAss.FullName, lsRoot + lsExt);
                }
            }
    }

// Вызов функции Assembly lAssembly = Assembly.LoadFile (lsExternalAssemblyPath); AddAssemblyReferencedAssemblies (lAssembly, System.IO.Path.GetDirectoryName (lsExternalAssemblyPath));

0 голосов
/ 19 июля 2009

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

Почему круговая зависимость:

Для создания новой библиотеки DLL требуется MyApp.exe;

MyApp.exe нельзя использовать без новой библиотеки DLL.

возможно, опубликуйте, какова ваша цель, и мы поможем правильно структурировать ваше приложение.

С должной ответственностью делегированный MyApp.exe должен заставить новую сгенерированную сборку работать, не требуя MyApp.exe для использования объектов из новой dll.

Пример. У вас должно быть только Выполнить на новой сгенерированной сборке .....

    public static void RenderTemplate(String templatepath, System.IO.Stream outstream, XElement xml, Dictionary<String, object> otherdata)
    {
        var templateFile = System.IO.File.ReadAllText(templatepath);

        var interpreter = new Interpreter();
        interpreter.ReferencedAssemblies.Add("System.Core.dll"); // linq extentions
        interpreter.ReferencedAssemblies.Add("System.Xml.dll");
        interpreter.ReferencedAssemblies.Add("System.Xml.Linq.dll");

        interpreter.UsingReferences.Add("System.Linq");
        interpreter.UsingReferences.Add("System.Xml");
        interpreter.UsingReferences.Add("System.Xml.Linq");
        interpreter.UsingReferences.Add("System.Collections.Generic");
        interpreter.Execute(templateFile, outstream, xml, otherdata);
    }
0 голосов
/ 22 января 2009

Похоже, что сборка, на которую вы ссылаетесь в динамически сгенерированной сборке, не входит в число стандартных путей поиска.

Где он находится относительно всего остального?

Вы должны запустить программу просмотра журнала Fusion , чтобы увидеть, что происходит неправильно.

0 голосов
/ 22 января 2009

Я думаю .Net пытается найти «SomeAdditionalReferencedDLL.dll» в вашей корзине или GAC. Вы пытались сделать Assembly.Load для "SomeAdditionalReferencedDLL.dll" перед созданием нового Blah?

0 голосов
/ 22 января 2009

Вы уверены, что при компиляции он ссылался на экземпляр библиотеки DLL, на которую, по вашему мнению, он ссылается? Возможно, он разрешает путь к чему-то другому, отличному от того, где вы думаете, так что при создании экземпляра вашего класса он больше не сможет найти эту DLL. Я рекомендую получить журнал Fusion как процесса компиляции, так и создания типов, чтобы увидеть, как типы разрешаются.

...