在C#中从程序集及其依赖项创建实例

时间:2009-01-22 18:00:01

标签: c# .net assemblies dependencies

我有一个应用程序(让我们称之为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");

稍后在我的应用程序中(或下次应用程序运行时)我尝试创建该类的实例。我知道新创建的类(我们称之为Blah)的DLL和基类的位置。我使用以下代码尝试创建新类的实例:

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

当我像上面那样调用Assembly.CreateInstance时,我收到错误消息,说它无法创建实例。当我检查assembly.GetReferencedAssemblies()它有我的应用程序(MyApp.exe)的标准引用和引用,但它没有我最初编译类时使用的依赖基类的引用(SomeAdditionalReferencedDLL.dll)

我知道我必须以某种方式添加基类引用才能创建实例,但我不知道如何做到这一点。当我拥有程序集及其所有依赖项时,如何从程序集创建类的实例?

由于

6 个答案:

答案 0 :(得分:3)

如果手动加载外部DLL(程序集),则不会自动加载您所引用的内容。

所以你必须创建一个AssemblyLoader。一个代码,用于检查程序集的Referenced程序集并自行加载它们。

有关驻留在计算机上奇数文件夹中的引用程序集的复杂性,而不是与编译的DLL一起,请查看AppDomain.CurrentDomain.AssemblyResolve事件。 (你使用它来欺骗.NET接受你的程序集被加载,即使它不在GAC或你编译的DLL中)

使用代码手动加载引用的DLL后,CreateInstance将起作用。

答案 1 :(得分:0)

您确定在编译时,它引用了您认为引用的DLL的实例吗?它可能正在解决除了你想到的地方之外的路径,这样在实例化你的类时,它再也找不到那个DLL了。我建议获取编译过程和类型创建的Fusion日志,以了解如何解析类型。

答案 2 :(得分:0)

我认为.Net正试图在您的bin或GAC中找到“SomeAdditionalReferencedDLL.dll”。在创建新的Blah之前,您是否尝试过为“SomeAdditionalReferencedDLL.dll”进行Assembly.Load?

答案 3 :(得分:0)

这听起来就像您在动态生成的程序集中引用的程序集不在标准探测路径之一。

它相对于其他一切存在于哪里?

你应该启动fusion log viewer以查看出错的地方。

答案 4 :(得分:0)

首先,我认为你有循环依赖..你的最后一段总结了它。 您需要重新考虑您的应用程序并确定您是否正确设置了职责。

为什么是循环依赖:

要生成新的dll,需要MyApp.exe;

如果没有新的dll,则无法使用MyApp.exe。

也许发布您的目标是什么,我们可以帮助您正确构建您的应用。

如果具有适当的职责,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);
    }

答案 5 :(得分:0)

//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);
                }
            }
    }

//函数调用 程序集lAssembly = Assembly.LoadFile(lsExternalAssemblyPath); AddAssemblyReferencedAssemblies(lAssembly,System.IO.Path.GetDirectoryName(lsExternalAssemblyPath));