检查实体是否实现接口,并在通用回购中添加谓词 [英] Check if entity implements interface and add predicate in generic repo

查看:122
本文介绍了检查实体是否实现接口,并在通用回购中添加谓词的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的一些实体有 IEnabledEntity 接口。
我想检查存储库,如果实体实现接口,然后添加一些谓词。我有以下代码:

Some of my entities have IEnabledEntity interface. I want to check in repository if entity implements interface then add some predicate. I have the following code:

public class Repository<T> : IRepository<T> where T : class, IEntity, new()
{
   public IQueryable<T> Get(Expression<Func<T, bool>> predicate, params string[] includes)
     IQueryable<T> query = Context.Set<T>();
     foreach (var include in includes)
     {
        query = query.Include(include);
     }

     query = query.Where(predicate);

     var isEnabledEntity = typeof(IEnabledEntity).IsAssignableFrom(typeof(T));

     if (isEnabledEntity)
     {
        query = query.Where(e => ((IEnabledEntity) e).IsEnabled);
     }

     return query;
}

 public interface IEnabledEntity
 {
    bool IsEnabled { get; set; }
 }

 public class Test : IBaseEntity, IEnabledEntity
 {
    // ...
    public bool IsEnabled { get; set; }
 }

但是,我有关于转换的例外:

But, I get exception about casting:

无法将类型Domain.Test转换为键入Domain.Interfaces.IEnabledEntity。 LINQ to Entities仅支持投影EDM原始或枚举类型。

如何使其工作?

推荐答案

Linq-to-Entities只知道是类的模型,这就是为什么表达式不能包含接口类型的原因。但是,如果 T 实现它,则可以运行时访问 IsEnabled 属性,所以如果您自己检查 IsAssignableFrom()(像你一样),可以使用 ExpressionVisitor 类来绕过转换:

Linq-to-Entities only knows models which are classes, that's why an expression can't contain an interface type. However clearly it's possible runtime to access the IsEnabled property if T implements it, so if you do the check yourself with IsAssignableFrom() (like you do), it's possible to use the ExpressionVisitor class to bypass the casting:

internal class IgnoreCast : ExpressionVisitor
{
    protected override Expression VisitUnary(UnaryExpression e)
    {
      if(e.NodeType == ExpressionType.Convert && e.Type.IsAssignableFrom(typeof(e.Operand))
         return e.Operand;
      else
         return e;
    }
}

然后,您需要使用extensionmethod创建您的过滤器, IgnoreCast class:

Then you need to create your filter with an extensionmethod which implements the IgnoreCast class:

internal static class LocalExtensions
{
   internal static IgnoreCast ic = new IgnoreCast();

   internal static IQueryable<T> FilterEnabled<T>(this IQueryable<T> query) where T: class
   {
      Expression<Func<T,bool>> expr = e => ((IEnabledEntity)e).IsEnabled;
      expr = (Expression<Func<T,bool>>)ic.Visit(e);
      return query.Where(expr);
   }
}

然后你可以在程序中使用该方法:

Then you can just use that method in your program:

if(typeof(IEnabledEntity).IsAssignableFrom(T))
   query = query.FilterEnabled();

基本方法访问(表达式e)将表达式的每个节点传递给该类节点的更专用的访问方法。 转换 nodetype是一个 UnaryExpression ,所以这个方法将在派生类中被覆盖。如果一元表达式是转换nodetype,并且操作数实现该类型,则它将返回操作数,从而删除该转换。

The base method Visit(Expression e) will pass each node of the expression to a more specialized Visit method for that kind of node. The Convert nodetype is a UnaryExpression so this method will be overriden in the derived class. If the unaryexpression is of the Convert nodetype and the operand implements the type it will just return the operand, thus removing the casting.

这篇关于检查实体是否实现接口,并在通用回购中添加谓词的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆