【问题标题】:Querying IQueryable completely decoupled from ORM?查询 IQueryable 与 ORM 完全解耦?
【发布时间】:2015-11-22 08:52:04
【问题描述】:

我可能完全误解了 IQueryable 背后发生的事情,所以如果我错了,欢迎指正。

我的数据库中有一个日志表,我想使用 Kendo UI 表在前端 Web 应用程序上查看它。

这些 javascript 组件真的很漂亮;他们可以根据多个维度来定位和限制来自连接的数据......例如,我可以获取按日志日期组织的前 100 条记录,过滤用户为“Joe User”的 DEBUG 或 INFO。

这适用于 IEnumerable<LogEntry> 对象就好了...但是表中有很多 LogEntry 对象,我认为随着日志表的持续增长,这应该会非常糟糕。

我想实现这样的服务:

public interface ILogBrowsingService
{
    IQueryable<LogEntry> Logs { get; }
}

...在另一个程序集中使用 ORM(EF、NHibernate、Telerik DataAccess,尚未决定)进行具体实现,这将允许将一些(希望是大多数)繁重的查询卸载到数据库服务器。我基本上会针对服务中暴露的 IQueryable 运行查询……但是,我想我很快就会遇到问题,因为该服务将在其自己的特定于服务的数据上下文和其自己的 Linq-To 下运行-X 实现。我相信我会遇到这个实现的问题,处理数据库上下文生命周期和 IQueryable 支持等等。

我正在考虑针对通用 IQueryable&lt;LogEntry&gt; 捕获“查询”并将其作为参数传递给服务以针对具体实现进行重播,但我不知道该怎么做,但是大致如下:

public interface ILogBrowsingService
{
    IList<LogEntry> GetLogEntryQueryResults(IQuery<LogEntry> query);
}

我不知道IQuery&lt;LogEntry&gt; 会是什么样子。

我真的不想尝试编写一个服务/存储库模式,该模式将包含该表的多种可搜索性排列。我是在想象一个没有问题的问题,还是有一种“正确”的方式来完成我正在尝试做的事情,完全与 ORM 后端无关?

【问题讨论】:

  • 只是不要使用 IQueryable。返回一个 IEnumerable 并且 never 告诉存储库 如何 做某事。只是要求做的事情。 repo 将知道如何使用 EF 和其他人返回实体。保持简单并为特定查询传递特定条件(一个简单对象)。

标签: c# entity-framework nhibernate orm iqueryable


【解决方案1】:

我使用了这种称为查询对象模式的模式,我相信它与您在此处尝试实现的相似。该模式不会自动记录查询并为您重放它们,但它允许您以可以重放的方式编写查询,并且还避免了存储库等模式的副作用。

我们将定义一个接口IQuery&lt;T&gt;,如下所示

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

在您的服务类中,您可以依赖IRunQuery(如果您正在使用依赖注入)并创建您要执行的查询对象的实例并将其传递给IRunQueryRun 方法实例。

这可能不适合您的情况,但您可以尝试使用此模式,看看它是否能带您进入任何地方。

【讨论】:

    猜你喜欢
    • 2010-11-27
    • 2016-05-22
    • 1970-01-01
    • 1970-01-01
    • 2020-08-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-29
    相关资源
    最近更新 更多