如何最好地通过配置动态创建和执行 .NET (C#) 类中的方法

发布于 2024-12-01 09:03:38 字数 1137 浏览 0 评论 0原文

我正在考虑类似于 MsBuild 中的“内联任务”的内容。供参考:http://msdn.microsoft.com/en-us/library/ dd722601.aspx

我想找到或创建一个框架,它允许我通过配置覆盖方法。例如,如果我有一个众所周知的基类,它有一个方法 Execute(args),我如何在部署时提供重写的方法实现,而不需要新的代码、构建、发布周期?我想将方法​​主体实际插入到配置文件或最好是数据库表中。

我认为这可以通过 code dom、动态语言集成或者类似 powershell(?) 之类的东西来完成。我正在寻找建议或者可能是某人已经编写的库。

该应用程序是用 C# 编写的。最好该扩展也采用 C# 语言,但我也愿意接受其他想法。

更新:从技术上讲,我什至不必实际重写方法。只需能够动态执行一些外部源代码,传入参数并返回结果就足够了。

更新。我最终编写了代码来实例化 PowerShell 对象并动态执行脚本以返回值。这是我使用的代码片段。

    public static Collection<PSObject> ExecuteScript(string code, string variableName, object variableValue)
        {
            PowerShell ps = PowerShell.Create();    

            ps.AddScript(code);

            if (!string.IsNullOrWhiteSpace(variableName))
            {                   
                ps.Runspace.SessionStateProxy.SetVariable(variableName, variableValue);
            }

            var result = ps.Invoke();

            return result;
        }

然后在调用代码中,我只需检查返回值中的第一个 PSObject,并从中提取结果值。效果很好。感谢您的所有回复。

I'm thinking of something along the lines of the "Inline Task" in MsBuild. For reference: http://msdn.microsoft.com/en-us/library/dd722601.aspx

I'd like to find or create a framework which allows me to override a method via configuration. For example if I have a well known base class which has a method Execute(args), how can I supply an overridden method implementation at deployment time, without requiring new code, build, release cycle? I would like to actually plug in the method body into a config file or preferably a database table.

I assume this would be done either with code dom, dynamic language integration, or perhaps something like powershell(?). I'm looking for recommendations or perhaps a library someone has already written.

The application is written in C#. Preferably the extension would also be in C#, but I'm open to other ideas as well.

Update: Technically I don't even have to actually override a method. It would be sufficient to just be able to dynamically execute some external source code, passing in an arg and returning a result.

Update. I ended up writing code to instantiate a PowerShell object and execute a script dynamically to return a value. Here is a snippet of code I used.

    public static Collection<PSObject> ExecuteScript(string code, string variableName, object variableValue)
        {
            PowerShell ps = PowerShell.Create();    

            ps.AddScript(code);

            if (!string.IsNullOrWhiteSpace(variableName))
            {                   
                ps.Runspace.SessionStateProxy.SetVariable(variableName, variableValue);
            }

            var result = ps.Invoke();

            return result;
        }

Then in the calling code, I simply check the first PSObject in the return value, and pull the resulting value from it. It works great. Thanks for all the responses.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

薄凉少年不暖心 2024-12-08 09:03:38

这是动态执行的两个示例。不过我没有使用过,所以我无法进一步评论。

http://www.codeproject.com/KB/dotnet/evaluator.aspx
http://www.csharpfriends.com/articles/getarticle.aspx?articleid=118

关于命名空间,从第二篇文章开始,您可以通过 CompilerParameter 类添加程序集。

// Create the C# compiler
CSharpCodeProvider csCompiler = new CSharpCodeProvider();
ICodeCompiler iCodeCompiler = csCompiler.CreateCompiler();

// input params for the compiler
CompilerParameters compilerParams = new CompilerParameters();
compilerParams.OutputAssembly = "CSharpFriends.dll";
compilerParams.ReferencedAssemblies.Add("system.dll");

Here are two examples of dynamic execution. I have used neither though so I can't comment further.

http://www.codeproject.com/KB/dotnet/evaluator.aspx
http://www.csharpfriends.com/articles/getarticle.aspx?articleid=118

Regarding namespaces, from the second article you can add assemblies through the CompilerParameter class.

// Create the C# compiler
CSharpCodeProvider csCompiler = new CSharpCodeProvider();
ICodeCompiler iCodeCompiler = csCompiler.CreateCompiler();

// input params for the compiler
CompilerParameters compilerParams = new CompilerParameters();
compilerParams.OutputAssembly = "CSharpFriends.dll";
compilerParams.ReferencedAssemblies.Add("system.dll");
ぃ弥猫深巷。 2024-12-08 09:03:38

一种选择是使用 Iron Python(或另一种 DLR 语言)。然后,您的 Execute 方法将在配置文件中查找脚本,编译它并在运行时执行它。

在您的项目中包含必要的 Iron Python 程序集并不是很大的开销。

您可能需要做一些管道工作才能将应用程序的其他部分暴露给 python 运行时环境,但这很容易做到。

One option would be to use Iron Python (or another DLR language). Your Execute method would then lookup the script in your configuration file, compile it and execute it all at runtime.

Including the necessary Iron Python assemblies with your project isn't a significant overhead.

You might need to do some plumbing to expose other parts of your application to the python runtime environment but this is quite easy to do.

私野 2024-12-08 09:03:38

您可以使用接口,然后在运行时解析具体的类,例如使用配置文件。
检查各种依赖注入容器 http://www.hanselman.com/blog/ListOfNETDependencyInjectionContainersIOC.aspx< /a>

You can use interfaces and then resolve the concrete classes at runtime e.g. using configuration files.
Check the various Dependency Injection Containers at http://www.hanselman.com/blog/ListOfNETDependencyInjectionContainersIOC.aspx

离线来电— 2024-12-08 09:03:38

托管可扩展性框架 (MEF) 可能也适合。它作为 .NET 4 的一部分包含在内。

http://msdn.microsoft.com /en-us/library/dd460648.aspx

http://mef.codeplex.com/

如果可扩展性仅针对一种方法,那么 MEF 就有点过分了。如果您所扩展的内容会随着时间的推移而增长,那么我认为 MEF 将提供最强大且长期可管理的框架。

Managed Extensibility Framework (MEF) might be suitable as well. It was included as part of .NET 4.

http://msdn.microsoft.com/en-us/library/dd460648.aspx

http://mef.codeplex.com/

If the extensibility is just for one method then MEF would be overkill. If what you are extending will grow over time then I think MEF would provide the most robust and long-term manageable framework.

黑色毁心梦 2024-12-08 09:03:38

看起来您可能想看看工厂模式;回国代表。不幸的是,您将需要一个类型来“容纳”该方法,因此您通常会生成如下代码:

namespace Dynamic {
  public static int Foo(int bar) {
     // .. Configured body here.
  }
}

重要的是您的工厂不会生成它以前见过的方法。这是一个例子:

static class Delegates
{
    private static Func<Func<int, string>> _test;
    public static Func<int, string> Test
    {
        get
        {
            return _test();
        }
    }

    static Delegates()
    {
        // Use your config variables instead of the "return arg.ToString();"
        CreateFactory<Func<int, string>>(x => _test = x, "return arg.ToString();");
    }

    private static void CreateFactory<TDelegate>(Action<Func<TDelegate>> locationSetter, string identifier)
    {
        locationSetter(() =>
            {
                var result = Generate<TDelegate>(identifier);
                locationSetter(() => result);
                return result;
            });
    }

    private static string GenerateSignature<TDelegate>()
    {
        // Create the signature of the delegate.
        var t = typeof(TDelegate);
        if (!typeof(Delegate).IsAssignableFrom(t))
            throw new Exception("TDelegate must be delegate type.");
        var invoke = t.GetMethod("Invoke");

        var sig = new StringBuilder();

        // Append the return type.
        if (invoke.ReturnType == typeof(void))
            sig.Append("void");
        else
            sig.Append(invoke.ReturnType.FullName);

        sig.Append(" ");
        sig.Append("Invoke(");

        // Append the parameters.
        var param = invoke.GetParameters();
        for (var i = 0; i < param.Length; i++)
        {
            if (i != 0)
                sig.Append(", ");
            sig.Append(param[i].ParameterType.FullName);
            sig.Append(" ");
            sig.Append(param[i].Name);
        }

        sig.Append(")");

        return sig.ToString();
    }

    private static TDelegate Generate<TDelegate>(string code)
    {
        // Generate the containing class and method.
        var codeBuilder = new StringBuilder(50);
        codeBuilder.AppendLine("using System;");
        codeBuilder.Append("namespace Dynamic { class DynamicClass { public static ");
        codeBuilder.Append(GenerateSignature<TDelegate>());
        codeBuilder.AppendLine("{");
        codeBuilder.AppendLine(code);
        codeBuilder.AppendLine("} } }");

        var compilerVersion = new Version(1, 0, 0, 0);

        // Create the compiler parameters.
        var parameters = new CompilerParameters();
        parameters.GenerateInMemory = true;
        parameters.GenerateExecutable = false;
        parameters.ReferencedAssemblies.Clear();
        foreach (var referenceAssembly in AppDomain.CurrentDomain.GetAssemblies())
        {
            parameters.ReferencedAssemblies.Add(referenceAssembly.Location);
            // Figure out which version we are compiling against.
            var an = new AssemblyName(referenceAssembly.FullName);
            if (an.Name == "mscorlib" && compilerVersion < an.Version)
            {
                compilerVersion = an.Version;
            }
        }

        var cp = new CSharpCodeProvider(
            new Dictionary<string, string>() { { "CompilerVersion", string.Format("v{0}.{1}", compilerVersion.Major, compilerVersion.Minor) } }
            );

        var results = cp.CompileAssemblyFromSource(parameters, codeBuilder.ToString());
        if (results.Errors.HasErrors)
            throw new Exception("Method failed to compile.");
        var assembly = results.CompiledAssembly;
        if (assembly == null)
            throw new Exception("Method failed to compile.");

        var t = assembly.GetType("Dynamic.DynamicClass");
        if (t == null)
            throw new Exception("Method failed to compile.");
        var m = t.GetMethod("Invoke");
        if (m == null)
            throw new Exception("Method failed to compile.");

        return (TDelegate)(object)Delegate.CreateDelegate(typeof(TDelegate), m);
    }
}

It looks like you might want to have a look at the Factory Pattern; returning delegates. Unfortunately you will need a type to 'house' the method, so you would typically generate code like:

namespace Dynamic {
  public static int Foo(int bar) {
     // .. Configured body here.
  }
}

It's important that your factory does not generate methods it has seen before. Here is an example:

static class Delegates
{
    private static Func<Func<int, string>> _test;
    public static Func<int, string> Test
    {
        get
        {
            return _test();
        }
    }

    static Delegates()
    {
        // Use your config variables instead of the "return arg.ToString();"
        CreateFactory<Func<int, string>>(x => _test = x, "return arg.ToString();");
    }

    private static void CreateFactory<TDelegate>(Action<Func<TDelegate>> locationSetter, string identifier)
    {
        locationSetter(() =>
            {
                var result = Generate<TDelegate>(identifier);
                locationSetter(() => result);
                return result;
            });
    }

    private static string GenerateSignature<TDelegate>()
    {
        // Create the signature of the delegate.
        var t = typeof(TDelegate);
        if (!typeof(Delegate).IsAssignableFrom(t))
            throw new Exception("TDelegate must be delegate type.");
        var invoke = t.GetMethod("Invoke");

        var sig = new StringBuilder();

        // Append the return type.
        if (invoke.ReturnType == typeof(void))
            sig.Append("void");
        else
            sig.Append(invoke.ReturnType.FullName);

        sig.Append(" ");
        sig.Append("Invoke(");

        // Append the parameters.
        var param = invoke.GetParameters();
        for (var i = 0; i < param.Length; i++)
        {
            if (i != 0)
                sig.Append(", ");
            sig.Append(param[i].ParameterType.FullName);
            sig.Append(" ");
            sig.Append(param[i].Name);
        }

        sig.Append(")");

        return sig.ToString();
    }

    private static TDelegate Generate<TDelegate>(string code)
    {
        // Generate the containing class and method.
        var codeBuilder = new StringBuilder(50);
        codeBuilder.AppendLine("using System;");
        codeBuilder.Append("namespace Dynamic { class DynamicClass { public static ");
        codeBuilder.Append(GenerateSignature<TDelegate>());
        codeBuilder.AppendLine("{");
        codeBuilder.AppendLine(code);
        codeBuilder.AppendLine("} } }");

        var compilerVersion = new Version(1, 0, 0, 0);

        // Create the compiler parameters.
        var parameters = new CompilerParameters();
        parameters.GenerateInMemory = true;
        parameters.GenerateExecutable = false;
        parameters.ReferencedAssemblies.Clear();
        foreach (var referenceAssembly in AppDomain.CurrentDomain.GetAssemblies())
        {
            parameters.ReferencedAssemblies.Add(referenceAssembly.Location);
            // Figure out which version we are compiling against.
            var an = new AssemblyName(referenceAssembly.FullName);
            if (an.Name == "mscorlib" && compilerVersion < an.Version)
            {
                compilerVersion = an.Version;
            }
        }

        var cp = new CSharpCodeProvider(
            new Dictionary<string, string>() { { "CompilerVersion", string.Format("v{0}.{1}", compilerVersion.Major, compilerVersion.Minor) } }
            );

        var results = cp.CompileAssemblyFromSource(parameters, codeBuilder.ToString());
        if (results.Errors.HasErrors)
            throw new Exception("Method failed to compile.");
        var assembly = results.CompiledAssembly;
        if (assembly == null)
            throw new Exception("Method failed to compile.");

        var t = assembly.GetType("Dynamic.DynamicClass");
        if (t == null)
            throw new Exception("Method failed to compile.");
        var m = t.GetMethod("Invoke");
        if (m == null)
            throw new Exception("Method failed to compile.");

        return (TDelegate)(object)Delegate.CreateDelegate(typeof(TDelegate), m);
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文