检查实体是否实现接口并在通用仓库中添加谓词

时间:2016-01-19 08:40:18

标签: c# entity-framework linq generics repository

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

IEnabledEntity

但是,我对铸造有例外:

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

如何让它发挥作用?

2 个答案:

答案 0 :(得分:2)

Linq-to-Entities只知道类的模型,这就是表达式不能包含接口类型的原因。但是,如果IsEnabled实现了T属性,那么访问IsAssignableFrom()属性的运行时很明显,所以如果您自己检查ExpressionVisitor(就像您一样),那么它就是“{1}}。可以使用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; } } 类绕过转换:

IgnoreCast

然后,您需要使用extensionmethod创建过滤器,该方法实现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); } } 类:

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

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

Visit(Expression e)

基本方法Convert会将表达式的每个节点传递给该类型节点的更专用的Visit方法。 UnaryExpression nodetype是-- create field ALTER TABLE users ADD COLUMN messages_total integer; ALTER TABLE users ALTER COLUMN messages_total SET DEFAULT 0; COMMENT ON COLUMN users.messages_total IS 'Users messages total.'; -- create trigger function CREATE OR REPLACE FUNCTION "users_messages::update_statistics"() RETURNS trigger AS $BODY$BEGIN IF TG_OP = 'DELETE' THEN UPDATE users SET messages_total = messages_total - 1 WHERE id = OLD.user_id; ELSIF TG_OP = 'INSERT' THEN UPDATE users SET messages_total = messages_total + 1 WHERE id = NEW.user_id; END IF; RETURN NULL; END;$BODY$ LANGUAGE plpgsql VOLATILE COST 5; CREATE TRIGGER update_statistics AFTER INSERT OR DELETE ON users_messages FOR EACH ROW EXECUTE PROCEDURE "users_messages::update_statistics"(); ,因此此方法将在派生类中重写。如果unaryexpression是Convert nodetype并且操作数实现了类型,它将只返回操作数,从而删除了转换。

答案 1 :(得分:2)

IQueryable<T>中的类型参数是协变的,因此不必担心在表达式中强制转换实体,只需安全地转换整个查询本身,然后使用Cast<T>()将其恢复为实体类型:

    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 enabledQuery = query as IQueryable<IEnabledEntity>;

        if (enabledQuery != null)
            query = enabledQuery.Where(e => e.IsEnabled).Cast<T>();

        return query;
    }