Как работать с динамическими объектами в качестве ввода для лямбда-выражений? - PullRequest
0 голосов
/ 08 ноября 2019

Я хочу сохранить выражение в базе данных, чтобы поддерживать механизм правил. Выражения в форме этого примера:

Expression<Func<MyTerm, bool>> expression = t => t.MyProperty == SomeValue;

Проблема, с которой я сталкиваюсь, заключается в том, что мне нужно создать MyTerm во время выполнения. Чтобы по-прежнему иметь возможность использовать MyTerm в качестве параметра для лямбда-выражения, я реализовал решение с ExpandoObject, например так:

public class MyTerm
{
    ExpandoObject ParameterSet;

    public void AddParameter(string name, object value)
    {
        IDictionary<string, object> dictionary = ParameterSet as IDictionary<string, object>;
        if (dictionary.ContainsKey(name))
        {
            dictionary[name] = value;
        }
        else
        {
            dictionary.Add(name, value);
        }
    }

    public string GetString(string name)
    {
        IDictionary<string, object> dictionary = ParameterSet as IDictionary<string, object>;
        if (dictionary.ContainsKey(name))
        {
            return (string)dictionary[name];
        }
        throw new KeyNotFoundException("Term does not contain parameter {name}.");
    }

Дело в том, что я получаю преимущество от возможности добавления параметровво время выполнения MyTerm, но я должен указать тип возвращаемого значения для каждого получателя, вместо возможности использовать приведенный выше синтаксис:

t.MyProperty

Есть ли способ использовать динамические объекты в качестве ввода для лямбда-выражений илиЕсть ли способы написать метод расширения для таких классов, чтобы вернуть объект, а не определенный тип? Я могу скомпилировать выражения следующим образом:

public static Func<MyTerm, bool> Compile(string body)
{
    ParameterExpression term = Expression.Parameter(typeof(MyTerm), typeof(MyTerm).Name);
    LambdaExpression exp = DynamicExpressionParser.ParseLambda(new[] { term }, typeof(bool), body);
    return (Func<DynamicTerm, bool>)exp.Compile();
}

static void Main(string[] args)
{
    MyTerm term = new MyTerm();
    term.AddParameter("SomeParameter", "SomeValue");

    Func<MyTerm, bool> IsGreaterThanSomeParameter = Compile("MyTerm.GetString(\"SomeParameter\")== SomeValue);

    bool result = IsGreaterThanSomeParameter(term);

    Console.WriteLine(result);
    Console.ReadLine();
}

Я хочу хранить лямбда-выражения, которые не зависят от конкретной реализации, поэтому, если другой сервис пытается скомпилировать эти выражения обратно в функцию, синтаксис должен бытьможно использовать из коробки. Без каких-либо рабочих добытчиков. Заранее спасибо.

1 Ответ

1 голос
/ 08 ноября 2019

Оказывается, что если у вашего объекта есть индексатор, System.Linq.Dynamic попытается вызвать его, если не найдет подходящее свойство.

public class DynamicTerm
{
    private readonly Dictionary<string, object> store = new Dictionary<string, object>();

    public void Set(string key, object value) => store[key] = value;
    public object this[string key] => store[key];
}

public static void Main()
{
    string body = "Term.SomeProperty == \"foo\"";
    ParameterExpression termExpr = Expression.Parameter(typeof(DynamicTerm), "Term");
    var exp = DynamicExpressionParser.ParseLambda(new[] { termExpr }, typeof(bool), body);
    var compiled = (Func<DynamicTerm, bool>)exp.Compile();

    var term = new DynamicTerm();
    term.Set("SomeProperty", "foo");

    Console.WriteLine(compiled(term));
}

Я не могу найтиспособ возврата типизированных данных (он преобразует обе стороны в объект): однако он работает со строками. Возможно, это отправная точка.

...