Динамические объекты IDynamicMetaObjectProvider и JScript.Net - PullRequest
1 голос
/ 22 августа 2010

У меня есть класс EcmaEval, который позволяет пользователям моего приложения выполнять произвольный javascript. Реализация класса находится в конце этого вопроса. Я разрешаю пользователям получать доступ к объекту «среды», который предоставляет методы и свойства, которые могут быть полезны для сценариев.

Проблема в том, что мне нужно предоставить динамический объект C # для JScript, но он не работает. Кто-нибудь делал это раньше - должно ли это работать?

Итак, если у меня есть простой старый объект со строковым свойством с именем name, он работает:

        test test = new test();
        test.Name = "Daniel Bryars";

        EcmaEval ecmaEval = new EcmaEval(new List<String>
                                             {
                                                 Assembly.GetExecutingAssembly().Location
                                             }, test);

        String ecma = "environment.Name";
        String result = ecmaEval.Eval<String>(ecma);

        Assert.AreEqual("Daniel Bryars", result);

НО, если я передаю свой объект EcmaEval динамический объект, то это не так (свойство Name имеет значение null):

        dynamic expandoObject = new ExpandoObject();
        expandoObject.Name = "Daniel Bryars";

        EcmaEval ecmaEval = new EcmaEval(new List<String>
                                             {
                                                 Assembly.GetExecutingAssembly().Location
                                             }, expandoObject);

        String ecma = "environment.Name";
        String result = ecmaEval.Eval<String>(ecma);
            Assert.AreEqual("Daniel Bryars", result);

(результат равен нулю.)

Вот реализация EcmaEval. Есть еще один класс, называемый JSObjectToDotNetConversion, который приводит JSObjects к объектам C # (он использует отражение для создания нового объекта C # и установки полей и / или свойств), но реализация этого класса не является подходящей.

using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.JScript;

namespace Aeriandi.ApplicationBlocks.BusinessBaseObjects.Ecma
{
    /// <summary>
    /// Exposes the JScrip eval function as a .net method.
    /// This uses the "safe" JScript.Eval so no disk, or network access is allowed.
    /// </summary>
    public class EcmaEval
    {
        private readonly object _evaluator;
        private readonly Type _evaluatorType;
        private readonly Object _environment;

        public EcmaEval() : this (new List<string>(), null )
        {            
        }

        public EcmaEval(List<String> referencedAssemblies, Object environment)
        {
            if (null == referencedAssemblies)
            {
                throw new ArgumentNullException("referencedAssemblies", "The argument referencedAssemblies must not be null");
            }

            _environment = environment;
            JScriptCodeProvider compiler = new JScriptCodeProvider();

            CompilerParameters parameters = new CompilerParameters();
            parameters.GenerateInMemory = true;

            foreach (String referencedAssembly in referencedAssemblies)
            {
                parameters.ReferencedAssemblies.Add(referencedAssembly);
            }

            string _jscriptSource =
@"package Evaluator
{
    class Evaluator
    {
        public function Eval(expr : String, environment : Object) 
        { 
            return eval(expr); 
        }
    }
}";
            CompilerResults results = compiler.CompileAssemblyFromSource(parameters, _jscriptSource);

            Assembly assembly = results.CompiledAssembly;
            _evaluatorType = assembly.GetType("Evaluator.Evaluator");
            _evaluator = Activator.CreateInstance(_evaluatorType);
        }

        public Object Eval(Type returnType, String ecmaScript)
        {
            ecmaScript = WrapInBrackets(ecmaScript);

            Object result = _evaluatorType.InvokeMember(
                     "Eval",
                     BindingFlags.InvokeMethod,
                     null,
                     _evaluator,
                     new object[] { ecmaScript, _environment }
                  );

            return JSObjectToDotNetConversion.Coerce(returnType, result);
        }

        public T Eval<T>(String ecmaScript)
        {
            return (T) Eval(typeof (T), ecmaScript);
        }

        private static String WrapInBrackets(String ecmaScript)
        {
            //You can't start a block of js with a { because it's ambiguous (according to the spec)
            //so we wrap everything in brackets.
            return String.Format("({0})", ecmaScript);
        }
    }
}

1 Ответ

0 голосов
/ 22 августа 2010

По сути, похоже, что я не могу сделать это с помощью JScript.Net.Я думаю, что мне нужно использовать что-то встроенное поверх DLR, например:

IronJS

http://github.com/fholm/IronJS

или

Управляемый JScript (которого больше нетразрабатывается MS.)

http://www.microsoft.com/downloads/details.aspx?familyid=A5189BCB-EF81-4C12-9733-E294D13A58E6&displaylang=en

Еще немного информации об Управляемом JScript и его окончании здесь: http://pietschsoft.com/post/2009/06/12/Managed-JScript-on-the-DLR-from-Microsoft-is-DEAD-Why.aspx

...