简体   繁体   English

是否可以转换表达式 <Func<T, bool> &gt;表达 <Func<MyType, bool> &gt;?

[英]Is it possible to convert Expression<Func<T, bool>> to Expression<Func<MyType, bool>>?

I have a repository class with some generic methods. 我有一个包含一些泛型方法的存储库类。 One is 一个是

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

For unit testing I have a TestRepository which uses in-memory objects instead of a database. 对于单元测试,我有一个TestRepository ,它使用内存中的对象而不是数据库。 The TestRepository overrides the FindAll method and I want to control what is returned. TestRepository重写了FindAll方法,我想控制返回的内容。 So I want to be able to do something like this: 所以我希望能够做到这样的事情:

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

But MyEntities.Where() only accepts a Expression<Func<MyEntity, bool>> . MyEntities.Where()只接受Expression<Func<MyEntity, bool>>

How can I cast/convert the generic expression to the strongly typed expression? 如何将通用表达式转换/转换为强类型表达式?

You can do something like this. 你可以做这样的事情。 Not sure whether it's a good idea, but it works. 不确定这是不是一个好主意,但它确实有效。 Basically, your overload can compare the type parameter T to your entity class. 基本上,您的重载可以将类型参数T与您的实体类进行比较。 If the predicate has the right type, you can cast. 如果谓词具有正确的类型,则可以进行强制转换。 Otherwise, you don't have anything to return. 否则,你没有任何东西可以归还。

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];
}

Usage: 用法:

var res = FindAll((MyEntity y) => y.x % 2 == 0).ToList();
Console.WriteLine(res.Count);

It looks like your repository implementation has a major flaw in a sense that the caller is not aware that he can only pass arguments of certain types (eg it looks like I can do FindAll<int>(v => v > 0) but in fact, the underlying implementation only works with MyEntity ). 看起来您的存储库实现存在一个主要缺陷,即调用者不知道他只能传递某些类型的参数(例如,它看起来我可以执行FindAll<int>(v => v > 0)但是事实上,底层实现仅适用于MyEntity )。 In other words, it's trying to be too "smart", it's not intuitive and error-prone. 换句话说,它试图太“聪明”,它不直观且容易出错。

One way of fixing this can be by introducing an interface/base class: 解决这个问题的一种方法是引入一个接口/基类:

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) { }
}

In the above example I'm referring to MyDbContext just for demonstration purposes (so it looks a bit more familiar if you worked with Entity Framework ). 在上面的例子中,我指的是MyDbContext仅用于演示目的(因此,如果您使用Entity Framework ,它看起来会更熟悉)。

Now you can instantiate MyEntityRepository and use it throughout the application. 现在,您可以实例化MyEntityRepository并在整个应用程序中使用它。 In case you're using some sort of IoC, you can modify the code slightly: 如果你正在使用某种IoC,你可以稍微修改一下代码:

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

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

And now you can easily inject and mock IMyEntityRepository within your application. 现在,您可以轻松地在应用程序中注入和模拟IMyEntityRepository

Hope this helps. 希望这可以帮助。

UPDATE UPDATE

As it turns out, since the given implementation is used for testing purposes only, you could try producing the expression of a desired type the following way: 事实证明,由于给定的实现仅用于测试目的,您可以尝试通过以下方式生成所需类型的表达式:

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

You can also apply some casting to the lambda parameter, if explicit one is needed. 如果需要显式的参数,您还可以对lambda参数应用一些强制转换。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM