如何在泛型类中映射默认 LINQ 表达式
本文关键字:默认 LINQ 表达式 映射 泛型类 | 更新日期: 2023-09-27 18:32:50
我有一个公开数据库 LINQ 提供程序的泛型Repo
类:
class Repo<T> where T : IPersisted
{
IQueryable<T> Get()
{
return _queryable;
}
}
(IPersisted
是持久对象的简单标记接口(。
现在。。。我想找到一种优雅的方法来为某些派生类型的IPersisted
注入默认的 LINQ 表达式。例如,对于以下 IPersisted 实现:
class Deletable : IPersisted
{
bool IsDeleted { get; set; }
}
我希望 IQueryable<T> Get()
方法仅当 T
属于 Deletable
类型时才返回 _queryable.Where(q => !q.IsDeleted)
。
我想过创建某种类型的字典映射IDictionary<Type, Expression>
,并在Get
方法中对typeof(T)
进行查找,但我不确定在这种情况下我是否能够强键入表达式。
如何根据T
的类型将"默认"LINQ 表达式注入Get
方法?我希望有一种可扩展的、面向对象的方式来将类型映射到默认表达式。
由于每个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
可能要注意的许多事情中的两件事:
它不会初始化任何东西或运行构造函数,但这对我们来说应该不是问题。
它相对较慢。应该没什么大不了的,因为您可以将实例缓存:)像这样:
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>
类之外没有意义,请使其显式。
不确定我们是否 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;
}
}