在运行时列出,加载和实例化App_Code中的未知类

时间:2009-04-25 17:38:58

标签: c#

我想在运行时加载并实例化位于App_Code文件夹中的类。

这些类实现了IModule

public interface IModule
{
    String Test();
}

测试类:

// ~/App_Code/Test.cs

namespace ModuleManagementSystem
{
    class Test : IModule
    {
        public String Test()
        {
            return "Testing! One, two, thee!";
        }
    }
}

类文件由系统用户上传,并充当模块。

模块的类名将始终与模块文件名相同。


我正在寻找功能正常的代码。

我会尽快给你赏金。


更新


namespace ModuleManagementSystem
{
    public sealed class Compile
    {
        public void ToDLL(String sourcefile, String outputfile)
        {
            var compilerResults =
            CodeDomProvider.CreateProvider("CSharp").CompileAssemblyFromFile(
                new CompilerParameters {
                    OutputAssembly = outputfile,
                    GenerateInMemory = false,
                    TreatWarningsAsErrors = false,
                    GenerateExecutable = false,
                },
                sourcefile
            );
        }
    }
}

上述工作正常:

// ~/App_Code/Test.cs

public class Test
{
    // ...
}

但是如果我实现我的IModule界面:

namespace ModuleManagementSystem
{
    public interface IModule
    {
        String HelloWorld();
    }
}

我收到以下错误:

  

{C:\的Inetpub \ wwwroot的\ ModuleManagementSystemWeb \ App_Code文件\ test.cs中(4,21)   :错误CS0246:类型或命名空间   名称'ModuleManagementSystem'可以   找不到(你错过了使用   指令或程序集   参考?)} System.CodeDom.Compiler.CompilerError

IModule接口和Compile类都位于同一个类库中,该类是从Web应用程序引用的:

http://roosteronacid.com/ModuleManagementSystem.jpg

4 个答案:

答案 0 :(得分:2)

实际上传到App_Code文件夹是一个坏主意。我会使用一个ASP.NET不知道的单独文件夹。这样你就不会让框架试图为你自动编译代码。

您可以使用CSharpCodeProvider轻松编译和运行代码 - 有关示例,请参阅my source code for Snippy

我希望这仅用于内部(和经过身份验证)的使用 - 我不建议让不受信任的用户在您的Web服务器上执行他们的代码。你可以通过运行非常低权限的代码使所有稍微更安全,但你仍然冒着代码紧密循环的风险等。

编辑:在回答您更新的问题时,您只需要提供适当的程序集以供参考,其中包含IModule。请参阅CompilerParameters.ReferencedAssembles属性。

答案 1 :(得分:1)

Assembly assembly = Assembly.Load(assemblyName);

IModule instance = assembly.CreateInstance(typeName) as IModule;

您也可以使用Activatore.CreateInstance()方法创建实例,而无需明确引用程序集。

答案 2 :(得分:0)

你可以使用反射来做到这一点,这是一个非常简单的方法,它会返回你传递给函数的模块名[class name]的实例

Public Shared Function GetDALInstance(ByVal moduleClassName As String) As IModule
        Dim className As String
        dim path as string = <<NamespacePath>>  
        className = Path + moduleClassName
        Return Assembly.Load(Path).CreateInstance(className)
    End Function

上面的方法是在vb.net中你可以简单地将其转换为c#

答案 3 :(得分:0)

我决定在运行时加载的类必须实现给定的接口,并且类的名称必须与文件名相同。

将类上传到并编译为的文件夹无关紧要。


加载已编译的类(.dll)并将其实例化:

return (IInterfaceClassesMustImplement)Assembly
/* continued --> */ .LoadFile("c:\...\SomeClass.dll")
/* continued --> */ .CreateInstance("SomeClass");

编译课程:

internal static class Compiler
{
    internal static void AssemblyFromFile(
        String classFilePath,
        String assemblyFilePath,
        String[] referencedAssemblies
    )
    {
        var cp = new CompilerParameters { OutputAssembly = assemblyFilePath };

        foreach (String ra in referencedAssemblies)
        {
            cp.ReferencedAssemblies.Add(ra);
        }

        CompilerResults cr = CodeDomProvider.CreateProvider("CSharp")
        /* continued --> */ .CompileAssemblyFromFile(cp, classFilePath);

        if (cr.Errors.Count > 0) // throw new Exception();
    }
}