基于DTO自动生成EF“where”子句

时间:2012-10-24 15:57:58

标签: c# entity-framework automapper

我正在尝试将参数自动生成为IQueryable.Where所以我可以从我的实体框架代码第一个数据上下文中选择实体,而无需编写和连接大量繁琐的映射代码。

我的项目包含一堆看似如下的DTO: -

class FooDto
{
  public string SomeProperty { get; set; }
  public string SomeOtherProperty { get; set; }
}

一堆看似如下的实体: -

class Foo
{
  public string SomeProperty { get; set; }
  public string SomeOtherProperty { get; set; }

  // Some other properties here.
}

DTO包含识别数据库中某些实体子集所必需的字段。我使用DTO来查询IQueryable实体: -

var result = queryable.Where(
  x => x.SomeProperty == dto.SomeProperty
    && x.SomeOtherProperty == dto.SomeOtherProperty)

实际属性各不相同,但查询的形状始终为“其中实体上的所有属性都与DTO上的所有匹配属性匹配”。没有更复杂的查询对象功能。

有许多DTO和实体。创建/维护和连接所有这些谓词是一个具有挑战性的架构问题。我们目前正在使用策略模式: -

public class FooDtoSelectStrategy : ISelectStrategy<FooDto, FooEntity>
{
  public Func<FooEntity, bool> GetPredicate(FooDto dto)
  {
    return x => x.SomeProperty == dto.SomeProperty
             && x.SomeOtherProperty == dto.SomeOtherProperty;
  }
}

除了一堆ninject绑定之外,我们已经有几十个这样的绑定,随着我们的域扩展,我们还会看到数百个。

我们有一个类似的挑战,将实体到DTO的值映射到我们使用AutoMapper解析的。

自动播放器(或类似工具)可以创建这些谓词并允许我们实现单个GenericPredicateProvider<TDto, TEntity>吗?

2 个答案:

答案 0 :(得分:0)

您正在做的事实上被称为specification pattern。所以你基本上已经重新设计了车轮,想出了一个基本相同的车轮。

您可以使用类似T4模板的东西来自动生成这些谓词提供程序,但是您查询同一对象的对象似乎有点愚蠢。你已经拥有了这个对象,那你为什么要查询所有字段呢?

答案 1 :(得分:0)

<强>更新: -

这是我的原型使用AutoMapper的类型映射来处理简单的情况。如果我想让它处理,例如,它将需要更多的工作。复杂的类型映射,但现在这是在做这项工作。

internal class AutoMapperSelectStrategy<TEntity, TIdentity> :
    IIdentitySelectStrategy<TEntity, TIdentity>
{
    private IMappingEngine mappingEngine;

    internal AutoMapperSelectStrategy(
        IMappingEngine<TEntity, TIdentity> mappingEngine)
    {
        this.mappingEngine = mappingEngine;
    }

    public Expression<Func<TEntity, bool>> GetPredicateForIdentity(
        TIdentity identity)
    {
        var entityParameter = Expression.Parameter(typeof(TEntity));

        var identityScope = Expression.MakeMemberAccess(
            Expression.Constant(
                new ExpressionScope<TIdentity>() { Value = identity }),
                typeof(ExpressionScope<TIdentity>).GetProperty("Value"));

        var equalityExpressions = this.MakeEqualityExpressions(
            identityScope, entityParameter);

        var aggregateEquality = equalityExpressions.Aggregate(
            (x, y) => Expression.AndAlso(x, y));

        var predicate = Expression.Lambda<Func<TEntity, bool>>(
            aggregateEquality, entityParameter);

        return predicate;
    }

    public IEnumerable<BinaryExpression> MakeEqualityExpressions(
        MemberExpression identityScope, ParameterExpression entityParameter)
    {
        var mapExpression =
            mappingEngine.CreateMapExpression<TIdentity, TEntity>();

        var body = mapExpression.Body as MemberInitExpression;
        var bindings = body.Bindings;

        foreach (var binding in bindings.OfType<MemberAssignment>())
        {
            var memberExpression = binding.Expression as MemberExpression;

            var left = Expression.Property(
                identityScope, memberExpression.Member as PropertyInfo);
            var right = Expression.Property(
                entityParameter, binding.Member as PropertyInfo);

            var equalityExpression = Expression.Equal(left, right);

            yield return equalityExpression;
        }
    }

    private class ExpressionScope<TDto>
    {
        public TDto Value { get; set; }
    }
}