2

我正在尝试将参数自动生成到 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 绑定之外,我们已经有了几十个这样的绑定,随着我们的域的扩展,我们正在寻找数百个。

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

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

4

2 回答 2

0

Update:-

Here's my prototype using AutoMapper's type mapping to handle the simple case. It'll need more work later if I want it to handle e.g. complex type mapping, but for now this is doing the job.

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; }
    }
}
于 2012-10-25T11:48:25.490 回答
0

您所做的实际上称为规范模式。所以你基本上重新设计了轮子,想出了一个稍微不同的轮子,基本上是一样的。

您可以使用诸如 T4 模板之类的东西来自动生成这些 Predicate 提供程序,但是您在一个对象中查询相同的对象似乎有点愚蠢。您已经拥有该对象,那么为什么要在所有字段中查询完全相同的内容呢?

于 2012-10-24T17:37:32.620 回答