4

我有一个Repo公开数据库 LINQ 提供程序的通用类:

class Repo<T> where T : IPersisted
{
    IQueryable<T> Get()
    {
        return _queryable;
    }
}

IPersisted是持久对象的简单标记接口)。

现在......我想找到一种优雅的方式来为某些派生类型的IPersisted. 例如,对于以下 IPersisted 实现:

class Deletable : IPersisted
{
    bool IsDeleted { get; set; }
}

我希望IQueryable<T> Get()方法返回_queryable.Where(q => !q.IsDeleted),只有当T是类型Deletable

我考虑过创建某种类型的字典映射,并在方法内部IDictionary<Type, Expression>进行查找,但我不确定在这种情况下我是否能够强输入表达式。typeof(T)Get

如何Get根据 的类型将“默认”LINQ 表达式注入方法中T我想要一种可扩展的、面向对象的方式将类型映射到默认表达式。

4

2 回答 2

3

由于每种IPersisted类型都会有自己的表达式,因此我会将其作为IPersisted. 所以现在你有了某种多态性。

就像是?

interface IPersisted<T> where T: IPersisted<T>
{
    Expression<Func<T, bool>> Predicate { get; }
}

class Repo<T> where T : IPersisted<T>, new()
{
    public IQueryable<T> Get()
    {
        var dummy = new T();
        return _queryable.Where(dummy.Predicate);
    }
}

class Deletable : IPersisted<Deletable>
{
    public Deletable()
    {

    }

    public Expression<Func<Deletable, bool>> Predicate
    {
        get { return x => !x.IsDeleted; }
    }

    bool IsDeleted { get; set; }
}

我认为您在这里需要的是某种静态多态性,但由于 C# 不提供,您可能需要为自己创建一个虚拟实例,只是为了获得表达式。


如果你不能有一个默认的构造函数,那么你可以依赖FormatterServices.GetUninitializedObject(t). 您可以Get像这样调整您的方法:

public IQueryable<T> Get()
{
    var dummy = (T)FormatterServices.GetUninitializedObject(typeof(T));
    return _queryable.Where(dummy.Predicate);
}

可能有很多事情需要注意的两件事FormatterServices.GetUninitializedObject

  1. 它不会初始化任何东西或运行构造函数,但这对我们来说应该不是问题。

  2. 它相对较慢。应该没什么大不了的,因为您可以缓存实例 :) 类似:

    class Repo<T> where T : IPersisted<T>
    {
        //caching mechanism: this is run only once per instance; you can make it 
        //static if this shud be run only once the entire lifetime of application
        readonly T dummy = (T)FormatterServices.GetUninitializedObject(typeof(T));
    
        public IQueryable<T> Get()
        {
            return _queryable.Where(dummy.Predicate);
        }
    }
    

如果确切的表达式不重要,那么您可以摆脱对象实例化。就像是:

interface IPersisted<T> where T: IPersisted<T>
{
    Func<T, bool> Predicate { get; }
}

class Repo<T> where T : IPersisted<T>
{
    public IQueryable<T> Get()
    {
        return _queryable.Where(x => x.Predicate(x));
    }
}

class Deletable : IPersisted<Deletable>
{
    public Func<Deletable, bool> Predicate
    {
        get { return x => !x.IsDeleted; }
    }
}

如果保留的原始定义IPersisted很重要,那么您可以将其设为非泛型。不确定这是否会使它的强类型降低。

interface IPersisted
{
    Expression<Func<object, bool>> Predicate { get; }
}

class Repo<T> where T : IPersisted
{
    public IQueryable<T> Get()
    {
        return _queryable.Where(dummy.Predicate);
    }
}

class Deletable : IPersisted
{
    public Expression<Func<object, bool>> Predicate
    {
        get { return x => !((Deletable)x).IsDeleted; }
    }
}

上面的方法可以通过寻找一个方法来进行更强的类型化,IPersisted但不需要足够好的约束:

interface IPersisted
{
    Expression<Func<T, bool>> GetPredicate<T>() where T : IPersisted;
}

class Repo<T> where T : IPersisted
{
    public IQueryable<T> Get()
    {
        return _queryable.Where(dummy.GetPredicate<T>());
    }
}

class Deletable : IPersisted
{
    Expression<Func<T, bool>> IPersisted.GetPredicate<T>() //made it explicit
    {
        return x => ((Deletable)(object)x).IsDeleted;
    }
}

注意:Predicate如果在类之外没有意义,请明确实现Repo<T>

于 2013-09-26T14:05:05.223 回答
0

不确定我们是否 100% 在同一页面上,但这个 get 方法示例将采用 linq Func,您可以将其用作 where 子句来过滤查询。

class Repo<T> where T : IPersisted
{
    IQueryable<T> Get(Func<T, bool> filter)
    {
        return _queryable.Where(filter);
    }
}

编辑:不确定这是否能很好地转化为 sql,但要确保我对你想要的东西有正确的想法(并且可能会发光):你考虑过这样的事情吗?

class Repo<T> where T : IPersisted
{
    IQueryable<T> Get()
    {
        if (typeof (T).IsAssignableFrom(typeof (IDeletable)))
        {
            return _queryable.Where(o => ((IDeletable) o).Deleted = false).AsQueryable();
        }
        return _queryable;
    }
}
于 2013-09-26T13:37:50.827 回答