是否可以转换Expression <func <t,bool =“”>&gt;到表达式<func <mytype,bool =“”>&gt;?</func <mytype,> </func <t,>

时间:2013-11-15 10:53:37

标签: c# unit-testing lambda

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

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>>

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

2 个答案:

答案 0 :(得分:1)

你可以这样做。不确定它是否是一个好主意,但它确实有效。基本上,您的重载可以将类型参数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);

答案 1 :(得分: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) { }
}

在上面的例子中,我指的是MyDbContext仅用于演示目的(如果你使用Entity Framework,它看起来会更熟悉。)

现在您可以实例化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参数应用一些强制转换。