如何在动态代码中设置属性

时间:2014-04-23 16:06:32

标签: c# roslyn

我希望能够将代码存储在数据库中,然后动态执行它(使用Roslyn)。但是,我希望能够从调用代码中注入(注入?)属性。见下文:

using Roslyn.Scripting.CSharp;
using RoslynMVCTest.Interfaces;

namespace RoslynMVCTest.Services
{
    public class MyService
    {
        private readonly IInjectedService _injectedService;

        public MyService(IInjectedService injectedService)
        {
            _injectedService = injectedService;
        }

        public bool SomeMethod()
        {
            string codeString = @"
using RoslynMVCTest.Interfaces;
public class SomethingDoer
{
    public IInjectedService InjectedService {get;set;}
    public static bool DoSomething()
    {
        return IInjectedService.SomeOtherMethod();
    }
}";
            var engine = new ScriptEngine();
            var session = engine.CreateSession(_injectedService);
            session.AddReference(this.GetType().Assembly);
            //How do I set the property in my dynamic code to _injectedService??
            var result = session.Execute<bool>("SomethingDoer.DoSomething()");
            return result;
        }
    }
}

我意识到这里可能存在语法和其他问题,但它很好地代表了我想要做的事情。有没有办法做到这一点?

1 个答案:

答案 0 :(得分:2)

首先,我将尽可能地回答与原始代码相符的问题。其次,我将展示一个更为简洁的例子,事实上可能就是你所追求的一切。

你可以肯定地声明你的类型,但是必须修复一些事情才能使它变得有意义。

  1. 尽管您尝试在静态方法中使用该属性,但您的SomethingDoer类声明了非静态InjectedService属性。为了讨论起见,我会假设您打算SomethingDoer.DoSomething也是非静态的,因此会使该类实现。

    public static bool DoSomething()
    

    要:

    public bool DoSomething()
    
  2. 您传递给CreateSession的“sesion”是您的实际服务。要理解为什么这不起作用,你必须理解你传递给CreateSession的论证意味着什么,以及它做了什么。 “会话”的含义是,该对象的所有公共属性都可作为原始标识符用于脚本会话,而无需.在任何目标上引用它们。因此,为了使代码正常工作,我引入了一个名为Session的新类(为方便起见,主要服务类内部):

    public class Session
    {
        public IInjectedService InjectedService { get; set; }
    }
    

    此外,我在调用CreateSession时使用了这个新类:

    var session = engine.CreateSession(new Session { InjectedService = _injectedService });
    

    这意味着您InjectedService内可以使用codeString属性。

  3. 也许最重要的是,您的代码codeString实际上从未被代码消耗过!您可以理解,您可以将此过程视为为代码设置字符串,然后想象您可以在其中调用一些任意方法。相反,只有一个代码块。因此,如果确实想要在脚本代码中声明一个完整的类,那么仍然必须直接消费脚本代码也是如此。这意味着codeString的最后两行应该看起来像:

    var somethingDoer = new SomethingDoer { InjectedService = InjectedService };
    somethingDoer.DoSomething()";
    

    这里我们实例化 SomethingDoer(因为更改1.)并通过会话提供的隐式InjectedService值设置服务属性(因为更改2) )。

  4. 为了完整起见,这里是完整的示例代码:

    namespace RoslynMVCTest.Interfaces
    {
        public interface IInjectedService
        {
            bool SomeOtherMethod();
        }    
    }
    
    namespace RoslynMVCTest.Services
    {
        using RoslynMVCTest.Interfaces;
    
        class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine(new MyService(new InjectedService()).SomeMethod());
                Console.ReadLine();
            }
        }
    
        class InjectedService : IInjectedService
        {
            public bool SomeOtherMethod()
            {
                return true;
            }
        }
    
        public class MyService 
        {
            private readonly IInjectedService _injectedService;
    
            public MyService(IInjectedService injectedService)
            {
                _injectedService = injectedService;
            }
    
            public class Session
            {
                public IInjectedService InjectedService { get; set; }
            }
    
            public bool SomeMethod()
            {
                string codeString = @"
    using RoslynMVCTest.Interfaces;
    public class SomethingDoer
    {
        public IInjectedService InjectedService { get; set; }
        public bool DoSomething()
        {
            return InjectedService.SomeOtherMethod();
        }
    }
    var somethingDoer = new SomethingDoer { InjectedService = InjectedService };
    somethingDoer.DoSomething()";
                var engine = new ScriptEngine();
                var session = engine.CreateSession(new Session { InjectedService = _injectedService });
                session.AddReference(this.GetType().Assembly);
                //How do I set the property in my dynamic code to _injectedService??
                var result = session.Execute<bool>(codeString);
                return result;
            }
        }
    }
    

    替代方法

    如果您只想让脚本运行一些与您的服务交互的代码,那么您可以看到,鉴于上述所有要点,这实际上是多么微不足道。因此,为了简明扼要地表达原始代码的意图,您所要做的就是:

    var result = session.Execute<bool>("InjectedService.SomeOtherMethod()");
    

    这里传递的代码只是冗长的第一个例子中服务方法的主体。很可能这就是你需要或想要的一切。