【问题标题】:How to build expression tree from string values如何从字符串值构建表达式树
【发布时间】:2020-12-05 09:57:41
【问题描述】:

我在别处问过一个具体问题,但在没有回应和一些调查之后,我把它归结为更通用的东西,但我仍在努力构建表达式树。

我正在使用第三方库,它使用接口和扩展方法进行一些映射。这些映射被指定为一个表达式树,我想做的是从字符串值构建该表达式树。

扩展方法签名:

public static T UpdateGraph<T>(this DbContext context, T entity, Expression<Func<IUpdateConfiguration<T>, object>> mapping = null, bool allowDelete = true) where T : class, new();

接口IUpdateConfiguration只是一个标记接口,但有以下扩展方法:

public static class UpdateConfigurationExtensions
{
    public static IUpdateConfiguration<T> OwnedCollection<T, T2>(this IUpdateConfiguration<T> config, Expression<Func<T, ICollection<T2>>> expression);
    public static IUpdateConfiguration<T> OwnedCollection<T, T2>(this IUpdateConfiguration<T> config, Expression<Func<T, ICollection<T2>>> expression, Expression<Func<IUpdateConfiguration<T2>, object>> mapping);
    public static IUpdateConfiguration<T> OwnedEntity<T, T2>(this IUpdateConfiguration<T> config, Expression<Func<T, T2>> expression);
    public static IUpdateConfiguration<T> OwnedEntity<T, T2>(this IUpdateConfiguration<T> config, Expression<Func<T, T2>> expression, Expression<Func<IUpdateConfiguration<T2>, object>> mapping);
}
  

使用示例实体:

public class Person
{
  public Car Car {get;set;}
  public House House {get;set;}
}

所以正常的显式用法是:

dbContext.UpdateGraph(person, mapping => mapping.OwnedEntity(p => p.House).OwnedEntity(p=> p.Car));

我需要做的是从属性名称列表构建该映射,

var props = {"Car","House"}

dbContext.UpdateGraph(person, buildExpressionFromStrings<Person>(props);

到目前为止:


static Expression<Func<IUpdateConfiguration<t>, object>> buildExpressionFromStrings<t>(IEnumerable<string> props)
{
   foreach (var s in props)
   {
        var single = buildExpressionFromString(s);
        somehow add this to chaining overall expression

    }      
}

static Expression<Func<IUpdateConfiguration<t>, object>> buildExpressionFromString<t>(string prop)
            {
                var ownedChildParam = Expression.Parameter(typeof(t));

                var ownedChildExpression = Expression.PropertyOrField(ownedChildParam, prop);

                var ownedChildLam = Expression.Lambda(ownedChildExpression, ownedChildParam);

                // Up to here I think we've built the (o => o.Car) part of map => map.OwnedEntity(o => o.Car)
// So now we need to build the map=>map.OwnedEntity(ownedChildLam) part, by calling Expression.Call I believe, but here I'm getting confused.
            }

实际上,现实世界的代码比这更复杂(需要处理递归和子属性/映射),但我认为一旦我为一个级别构建了表达式,我就可以对其进行排序。一天多来,我一直在努力解决这个问题……为了提供一些上下文,我使用实体框架和一些配置来定义聚合根。

【问题讨论】:

    标签: c# entity-framework linq expression-trees


    【解决方案1】:

    没什么好说的。这里

    mapping => mapping.OwnedEntity(p => p.House).OwnedEntity(p=> p.Car)
    

    lambda 表达式的部分body 不是 lambda 表达式,而只是链式方法调用表达式,第一个使用 lambda 表达式参数,下一个使用前一个结果。

    其次,扩展方法只是静态方法调用 C# 糖,在表达式树中它们必须作为静态方法“调用”。

    所以,建立一个调用

    public static IUpdateConfiguration<T> OwnedEntity<T, T2>(this IUpdateConfiguration<T> config, Expression<Func<T, T2>> expression);
    

    可能是这样的

    static Expression BuildConfigurationCall<T>(Expression config, string propertyName)
    {
        var parameter = Expression.Parameter(typeof(T), "it");
        var property = Expression.Property(parameter, propertyName);
        var selector = Expression.Lambda(property, parameter);
        return Expression.Call(
            typeof(UpdateConfigurationExtensions),
            nameof(UpdateConfigurationExtensions.OwnedEntity),
            new [] { typeof(T), property.Type },
            config,
            selector);
    }
    

    而有问题的 lambda 表达式将是:

    static Expression<Func<IUpdateConfiguration<T>, object>> BuildConfigurationExpression<T>(IEnumerable<string> propertyNames)
    {
        var parameter = Expression.Parameter(typeof(IUpdateConfiguration<T>), "config");
        var body = propertyNames.Aggregate((Expression)parameter,
            (config, propertyName) => BuildConfigurationCall<T>(config, propertyName));
        return Expression.Lambda<Func<IUpdateConfiguration<T>, object>>(body, parameter);
    }
    

    【讨论】:

      【解决方案2】:

      我使用 dynamic object creating 的对象生成器和给定的道具。我将DynamicQueryable 用于基于字符串的表达式。

      【讨论】:

      • 请仅在 cmets 中要求澄清。如果问题需要详细信息,请将其标记为“需要详细信息或明确性”。
      猜你喜欢
      • 1970-01-01
      • 2021-11-26
      • 2017-09-19
      • 1970-01-01
      • 1970-01-01
      • 2010-10-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多