查询 IQueryable 与 ORM 完全分离?
Querying IQueryable completely decoupled from ORM?
我可能完全误解了 IQueryable 背后发生的事情,所以如果我错了,欢迎指正。
我的数据库中有一个日志 table,我想使用 Kendo UI table 在前端 Web 应用程序上查看它。
这些 javascript 组件真的很漂亮;他们可以基于多个维度来定位和限制通过连接传来的数据……例如,我可以获得按日志日期组织的前 100 条记录,过滤为 DEBUG 或 INFO,其中用户 "Joe User".
这对 IEnumerable<LogEntry>
对象很好...但是 table 中有很多 LogEntry
对象,我认为这应该表现得很差,因为日志 table 继续增长。
我想实现这样的服务:
public interface ILogBrowsingService
{
IQueryable<LogEntry> Logs { get; }
}
...在另一个程序集中使用 ORM(EF、NHibernate、Telerik DataAccess 尚未决定)的具体实现,这将允许将查询的一些(希望是大部分)繁重工作卸载到数据库服务器。我基本上是 运行ning 针对服务中公开的 IQueryable 的查询...但是,我想我很快就会 运行 遇到问题,因为此服务将 运行ning 在它自己的特定于服务的数据上下文,以及它自己的 Linq-To-X 实现。我相信我会遇到此实现的问题,处理数据库上下文生命周期和 IQueryable 支持等等。
我正在考虑针对通用 IQueryable<LogEntry>
捕获 "query" 并将其作为参数传递给服务以针对具体实现重放,但我不知道如何我会这样做,但有些事情是这样的:
public interface ILogBrowsingService
{
IList<LogEntry> GetLogEntryQueryResults(IQuery<LogEntry> query);
}
我不知道 IQuery<LogEntry>
会是什么样子。
我真的不想尝试编写一个服务/存储库模式来合并此 table 的多种可搜索性排列。我是在想 none 的问题,还是有 "proper" 的方法来完成我想做的事情,完全不了解 ORM 后端?
我已经使用了这种称为查询对象模式的模式,我相信它与您在此处尝试实现的类似。该模式不会自动记录查询并为您重放它们,但它允许您以可以重放的方式编写查询,并且还避免了存储库等模式的副作用。
我们将定义一个接口 IQuery<T>
如下
public interface IQuery<T> where T : EntityBase<T>
{
IEnumerable<T> Run(IQueryable<T> queryable);
}
如果您没有通用实体基础类型的实现,您可以忽略 where
约束。
然后我们将根据我们查询的内容对该接口进行非常具体的实现。例如,我们可以实现一个查询来根据它们是 DEBUG、INFO 还是 ERROR
来获取日志条目
public class LogEntriesByTypeQuery : IQuery<LogEntry>
{
private readonly int type;
public LogEntriesByTypeQuery(string type)
{
this.type = type;
}
public IEnumerable<LogEntry> Run(IQueryable<LogEntry> logEntries)
{
return (from e in logEntries
where e.Type == type
select e).ToList();
}
}
我们现在可以根据我们想从数据库中获取什么来编写非常具体的查询。您可以组合多个筛选条件或为每个条件编写单独的查询。与规范模式不同,此模式不会让您能够将多个不同的查询组合在一起。
然后我们需要一些东西来 运行 这个查询。一个QueryRunner
可以派上用场
public class QueryRunner : IRunQuery
{
private readonly ISession session;
public QueryRunner(ISession session)
{
this.session = session;
}
public IEnumerable<T> Run<T>(IQuery<T> query) where T : EntityBase<T>
{
return query.Run(session.Query<T>());
}
}
在您的服务 class 中,您可以依赖 IRunQuery
(如果您正在使用依赖注入)并创建您要执行的查询对象的实例并将其传递给Run
IRunQuery
实例的方法。
这可能不适合您的情况,但您可以尝试使用此模式,看看它是否能带您进入任何地方。
我可能完全误解了 IQueryable 背后发生的事情,所以如果我错了,欢迎指正。
我的数据库中有一个日志 table,我想使用 Kendo UI table 在前端 Web 应用程序上查看它。
这些 javascript 组件真的很漂亮;他们可以基于多个维度来定位和限制通过连接传来的数据……例如,我可以获得按日志日期组织的前 100 条记录,过滤为 DEBUG 或 INFO,其中用户 "Joe User".
这对 IEnumerable<LogEntry>
对象很好...但是 table 中有很多 LogEntry
对象,我认为这应该表现得很差,因为日志 table 继续增长。
我想实现这样的服务:
public interface ILogBrowsingService
{
IQueryable<LogEntry> Logs { get; }
}
...在另一个程序集中使用 ORM(EF、NHibernate、Telerik DataAccess 尚未决定)的具体实现,这将允许将查询的一些(希望是大部分)繁重工作卸载到数据库服务器。我基本上是 运行ning 针对服务中公开的 IQueryable 的查询...但是,我想我很快就会 运行 遇到问题,因为此服务将 运行ning 在它自己的特定于服务的数据上下文,以及它自己的 Linq-To-X 实现。我相信我会遇到此实现的问题,处理数据库上下文生命周期和 IQueryable 支持等等。
我正在考虑针对通用 IQueryable<LogEntry>
捕获 "query" 并将其作为参数传递给服务以针对具体实现重放,但我不知道如何我会这样做,但有些事情是这样的:
public interface ILogBrowsingService
{
IList<LogEntry> GetLogEntryQueryResults(IQuery<LogEntry> query);
}
我不知道 IQuery<LogEntry>
会是什么样子。
我真的不想尝试编写一个服务/存储库模式来合并此 table 的多种可搜索性排列。我是在想 none 的问题,还是有 "proper" 的方法来完成我想做的事情,完全不了解 ORM 后端?
我已经使用了这种称为查询对象模式的模式,我相信它与您在此处尝试实现的类似。该模式不会自动记录查询并为您重放它们,但它允许您以可以重放的方式编写查询,并且还避免了存储库等模式的副作用。
我们将定义一个接口 IQuery<T>
如下
public interface IQuery<T> where T : EntityBase<T>
{
IEnumerable<T> Run(IQueryable<T> queryable);
}
如果您没有通用实体基础类型的实现,您可以忽略 where
约束。
然后我们将根据我们查询的内容对该接口进行非常具体的实现。例如,我们可以实现一个查询来根据它们是 DEBUG、INFO 还是 ERROR
来获取日志条目public class LogEntriesByTypeQuery : IQuery<LogEntry>
{
private readonly int type;
public LogEntriesByTypeQuery(string type)
{
this.type = type;
}
public IEnumerable<LogEntry> Run(IQueryable<LogEntry> logEntries)
{
return (from e in logEntries
where e.Type == type
select e).ToList();
}
}
我们现在可以根据我们想从数据库中获取什么来编写非常具体的查询。您可以组合多个筛选条件或为每个条件编写单独的查询。与规范模式不同,此模式不会让您能够将多个不同的查询组合在一起。
然后我们需要一些东西来 运行 这个查询。一个QueryRunner
可以派上用场
public class QueryRunner : IRunQuery
{
private readonly ISession session;
public QueryRunner(ISession session)
{
this.session = session;
}
public IEnumerable<T> Run<T>(IQuery<T> query) where T : EntityBase<T>
{
return query.Run(session.Query<T>());
}
}
在您的服务 class 中,您可以依赖 IRunQuery
(如果您正在使用依赖注入)并创建您要执行的查询对象的实例并将其传递给Run
IRunQuery
实例的方法。
这可能不适合您的情况,但您可以尝试使用此模式,看看它是否能带您进入任何地方。