【问题标题】:Cancelling an Entity Framework Query取消实体框架查询
【发布时间】:2011-02-28 15:30:11
【问题描述】:

我正在为 WinForms 应用程序编写查询管理器,除此之外,它需要能够在用户输入查询时向他们提供实时搜索结果(想想 Google 的实时结果) ,尽管显然是在胖客户端环境而不是 Web 环境中)。由于结果需要在用户键入时开始到达,因此搜索将变得越来越具体,所以我希望能够在用户输入更具体的信息时取消查询(因为结果会无论如何,只是被丢弃)。

如果这是普通的 ADO.NET,我显然可以使用 DbCommand.Cancel 函数并完成它,但我们使用 EF4 进行数据访问,似乎没有明显的取消方法一个问题。此外,在 Reflector 中打开 System.Data.Entity 并查看 EntityCommand.Cancel 会显示令人沮丧的空方法体,尽管 docs 声称调用它会将其传递给提供程序命令的相应 Cancel 函数。

我考虑过简单地让现有查询运行并启动一个新上下文来执行新搜索(并在完成后处理现有查询),但我不喜欢单个客户端拥有一个当我只对最近一个的结果感兴趣时,大量打开的数据库连接运行并行查询。

所有这一切都让我相信,一旦 EF 查询被分派到数据库,就根本无法取消它,但我希望这里有人能够指出我忽略的一些事情。

TL/DR 版本:是否可以取消当前正在执行的 EF4 查询?

【问题讨论】:

标签: entity-framework entity-framework-4


【解决方案1】:

看起来您在 EF 中发现了一些错误,但是当您将其报告给 MS 时,它将被视为文档中的错误。无论如何,我不喜欢直接与EntityCommand 互动的想法。这是我的示例如何终止当前查询:

var thread = new Thread((param) =>
    {
        var currentString = param as string;

        if (currentString == null)
        {
            // TODO OMG exception
            throw new Exception();
        }

        AdventureWorks2008R2Entities entities = null;
        try // Don't use using because it can cause race condition
        {
            entities = new AdventureWorks2008R2Entities();

            ObjectQuery<Person> query = entities.People
                .Include("Password")
                .Include("PersonPhone")
                .Include("EmailAddress")
                .Include("BusinessEntity")
                .Include("BusinessEntityContact");
            // Improves performance of readonly query where
            // objects do not have to be tracked by context
            // Edit: But it doesn't work for this query because of includes
            // query.MergeOption = MergeOption.NoTracking;

            foreach (var record in query 
                .Where(p => p.LastName.StartsWith(currentString)))
            {
                // TODO fill some buffer and invoke UI update
            }
        }
        finally
        {
            if (entities != null)
            {
                entities.Dispose();
            }
        }
    });

thread.Start("P");
// Just for test
Thread.Sleep(500);
thread.Abort();

这是我在 30 分钟后玩 if 的结果,所以它可能不应该被视为最终解决方案。我发布它是为了至少获得一些关于此解决方案可能导致的问题的反馈。要点是:

  • 上下文在线程内部处理
  • 结果未被上下文跟踪
  • 如果你杀死线程查询被终止并且上下文被释放(连接被释放)
  • 如果在启动新线程之前终止线程,则仍应使用一个连接。

我在 SQL 分析器中检查了查询是否已启动和终止。

编辑:

顺便说一句。另一种简单地停止当前查询的方法是在枚举中:

public IEnumerable<T> ExecuteQuery<T>(IQueryable<T> query)
{
    foreach (T record in query)
    {
        // Handle stop condition somehow
        if (ShouldStop())
        {
            // Once you close enumerator, query is terminated
            yield break;
        }
        yield return record;
    }
}

【讨论】:

  • 有趣;作为澄清的一点,我并不是建议与EntityCommand 交互是正确的方法,但方法主体为空的事实意味着它可能在更高级别上的可能性微乎其微。这是一个有趣的场景,最终可能会起作用,但现在我确实需要更改跟踪,如果可能的话,所以处理上下文不是我准备做的事情。如果找不到替代品,我会接受。
  • @Adam:好的,我明天会考虑使用更改跟踪的场景,因为它会产生新的后果——即使您取消查询,所有加载的实例也将被上下文跟踪。我最初的建议是每次搜索都有新的上下文,但我必须考虑一下。
  • 我并不真正关心在加载时取消查询。使用上述方法很容易做到这一点。我正在寻找的是在 query 阶段(当它在服务器端执行时)取消它。
  • 根据我在 EF6 中的测试不起作用。即使线程被中止,查询也会继续在数据库服务器上运行。如果我改为处理上下文,它会阻塞,直到查询完成。如果我关闭枚举器,它会阻塞直到查询完成。如果我关闭连接,它会阻塞,直到查询完成。我想知道这种行为是否改变了——因为@LadislavMrnka 和其他人似乎认为这应该有效。
猜你喜欢
  • 2022-01-15
  • 1970-01-01
  • 1970-01-01
  • 2021-12-25
  • 2013-03-17
  • 2014-02-24
  • 1970-01-01
相关资源
最近更新 更多