7

我有一个带有一些通用方法的存储库类。一个是

public IEnumerable<T> FindAll<T>(Expression<Func<T, bool>> predicate) where T : class
{
    return GetDbSet<T>().Where(predicate);
}

对于单元测试,我有一个TestRepository使用内存对象而不是数据库的。TestRepository覆盖该方法FindAll,我想控制返回的内容。所以我希望能够做这样的事情:

public override IEnumerable<T> FindAll<T>(Expression<Func<T, bool>> predicate)
{
    return MyEntities.Where(predicate).Cast<T>();
}

MyEntities.Where()只接受一个Expression<Func<MyEntity, bool>>.

如何将泛型表达式转换/转换为强类型表达式?

4

2 回答 2

2

你可以做这样的事情。不确定这是否是一个好主意,但它确实有效。基本上,您的重载可以将类型参数T与您的实体类进行比较。如果谓词具有正确的类型,则可以强制转换。否则,您将没有任何回报。

public class MyEntity { public int x; }

MyEntity[] MyEntitiesList = Enumerable.Range(1,5).Select(y => new MyEntity() { x = y }).ToArray();

public IEnumerable<T> FindAll<T>(Expression<Func<T, bool>> predicate)
{
     if (typeof(T) == typeof(MyEntity))
     {
         return (IEnumerable<T>)MyEntitiesList.Where((predicate as Expression<Func<MyEntity, bool>>).Compile());
     }
     return new T[0];
}

用法:

var res = FindAll((MyEntity y) => y.x % 2 == 0).ToList();
Console.WriteLine(res.Count);
于 2013-11-15T11:47:47.410 回答
0

从某种意义上说,您的存储库实现似乎存在重大缺陷,即调用者不知道他只能传递某些类型的参数(例如,看起来我可以这样做FindAll<int>(v => v > 0),但实际上,底层实现仅适用于MyEntity)。换句话说,它试图变得过于“聪明”,它不直观且容易出错。

解决此问题的一种方法是引入接口/基类:

public interface IRepository<T>
{
  IEnumerable<T> FindAll<T>(Expression<Func<T, bool>> predicate);
}

// A base class that can carry helper functionality.
public abstract class Repository<T> : IRepository<T>
{
  private readonly IEnumerable<T> _entities;

  protected Repository(IEnumerable<T> entities)
  {
    _entities = entities;
  }

  public IEnumerable<T> FindAll(Expression<Func<T, bool>> predicate)
  {
    return _entities.Where(predicate);
  }
}

// Concrete implementation
public class MyEntityRepository : Repository<MyEntity>
{
  public MyEntityRepository() : base(new MyDbContext().MyEntities) { }
}

在上面的示例中,我仅出于演示目的而引用(因此,如果您使用Entity FrameworkMyDbContext,它看起来会更熟悉一些)。

MyEntityRepository现在您可以在整个应用程序中实例化和使用它。如果您使用某种 IoC,您可以稍微修改代码:

public interface IMyEntityRepository : IRepository<MyEntity>
{
  // ...
}

public class MyEntityRepository : Repository<MyEntity>, IMyEntityRepository
{
  // ...
}

现在您可以轻松地在您的应用程序中注入和模拟IMyEntityRepository

希望这可以帮助。

更新

事实证明,由于给定的实现仅用于测试目的,您可以尝试通过以下方式生成所需类型的表达式:

return MyEntities.Where(Expression.Lambda<Func<MyEntity, bool>>(predicate.Body, 
  predicate.Parameters)).Cast<T>();

如果需要显式转换,您还可以对 lambda 参数应用一些转换。

于 2013-11-15T11:28:56.837 回答