【问题标题】:Best way to query using EF使用 EF 进行查询的最佳方式
【发布时间】:2013-07-10 15:50:13
【问题描述】:

使用 LINQ,我无法以有效的方式查询我的 DbContext。 该数据库包含 700,000 多个具有日期和名称以及其他信息的实体。

在我的代码中,我有一个新的对象列表(可能有 100,000 个元素)进来,我想查询我的数据库并扣除哪些信息是新实体或哪些信息是需要更新的现有实体.

我想以一种非常有效的方式进行(如果可能,使用单个查询)。

这是我的代码:

public class MyDbContext : DbContext
    {
        public DbSet<MyEntity> MyEntities { get; set; }
    }

    public class MyEntity
    {
        [Key]
        public Guid Id { get; set; }
        public DateTime Date { get; set; }
        public string Name { get; set; }
        public double Amount { get; set; }
        public string Description { get; set; }
    }

    public class IncomingInfo
    {
        public DateTime Date { get; set; }
        public string Name { get; set; }
        public double Amount { get; set; }
    }

    public class Modifier
    {
        public void AddOrUpdate(IList<IncomingInfo> info)
        {
            using (var context = new MyDbContext())
            {                 
                //Find the new information 
                //to add as new entities
                IEnumerable<MyEntity> EntitiesToAdd = ??

                //Find the information 
                //to update in existing entities
                IEnumerable<MyEntity> EntitiesToUpdate = ?? 
            }
        }
    }

有人可以帮我构建查询吗? 非常感谢。

编辑: 对不起,我忘了解释我如何认为两个实体相等。 如果 Date 和 Name 属性相同,则相等。

我首先尝试使用 LinqKit PredicateBuilder 构建谓词,但没有成功(遇到参数太大的错误,必须进行多次查询,耗时)。

到目前为止,我发现的最成功的方法是实现 LEFT OUTER join 并将传入列表加入 DbSet 我是这样实现的:

var values = info.GroupJoin(context.MyEntities,
                    inf => inf.Name + inf.Date.ToString(),
                    ent => ent.Name + ent.Date.ToString(),
                    (inf, ents) => new { Info = inf, Entities = ents })
                 .SelectMany(i => i.Entities.DefaultIfEmpty(),
                    (i, ent) => new { i.Info.Name, i.Info.Amount, i.Info.Date, ToBeAdded = ent == null ? true : false });

IEnumerable<MyEntity> EntitiesToAdd = values.Where(i => i.ToBeAdded)
    .Select(i => new MyEntity
    {
        Id = Guid.NewGuid(),
        Amount = i.Amount,
        Date = i.Date,
        Name = i.Name,
        Description = null
    }).ToList();

我的测试在数据库中包含 700,000 个实体。传入的信息列表包含 70,000 个项目;其中 50,000 个是现有实体,20,000 个是新实体。 执行此查询大约需要 15 秒,这对我来说似乎不合适。

希望这足以寻求帮助。有人可以帮我一个吗? 非常感谢。

【问题讨论】:

  • 你能告诉我们你已经尝试过什么吗?
  • 是否通过Guid Id 属性知道两个实体是否应该被视为相同?
  • 哇,人们的速度太快了,不能直接关闭和关闭 - 正在回复中,希望能回答这个问题。现在,甚至无法开始与用户的对话(至少通过答案,将不得不离开现场):-/ 希望有一个选项来对保留提出异议。
  • 这是我的回答:pastebin.com/pVv6bACW 基本总结,在 100,000 次潜在更新中,您将遇到 EF 的限制,并且必须做一些额外的工作来保持速度。
  • 我用我目前发现的内容和我的问题编辑了我的问题。希望这是一个足够的问题。

标签: c# linq entity-framework


【解决方案1】:

我阅读了来自 @Leniency 的 pastebin 回复,它涵盖了我要说的一些相同内容,例如查询日期范围并在那里进行比较。但是,该方法的问题在于(取决于这些日期的设置方式)它可能会返回数据库中的所有 700K+ 记录,这会给您带来绝对最差的性能。

我的建议是您分析您的网络拓扑,看看您对数据库的调用到底有多昂贵。我假设这是在(Web)服务器上运行的,该服务器从客户端接收这些 IncomingInfo 对象。如果此服务器与您的数据库服务器紧密连接(或在同一台机器上),那么您最好不要优化对数据库的调用。

此外,如果您可以控制客户端的行为,您可能希望强制他们在每个请求中仅发送 25 到 100 条记录。这将使您可以以更易于管理的方式处理它们。客户端可能必须向服务器发送 100 个或更多请求(您可以执行异步操作,以便它们一次发送约 5 个,具体取决于预期的负载配置文件),但至少它不会坐在那里超过 5 个等待从服务器返回单个请求的响应的分钟数。

顺便说一句,你所说的GroupJoin 调用需要 15 秒,可能是在加入之前必须下载所有 700K 记录。你看,连接不能在同一台机器上不存在的对象上完成,它要么必须将所有 IncomingInfo 对象(或至少 Name+Date.ToString() 连接)发送到数据库,或者它必须在完成任何连接之前从数据库中请求所有记录。您可能必须查看发送到数据库的 SQL 以判断正在使用哪种方法。但是您可能会发现,在这种情况下,一次查询一个匹配项可能比连接要快。

希望对您有所帮助! ;)

【讨论】:

  • 我确实对客户端有某种“排序”的控制权。我将要求他们一次只发送一个特定日期的数据,并使用名称和日期范围的 where 条件检索数据。那应该行得通!谢谢。
  • @HectorTwist - 在不了解您正在使用的数据类型及其使用方式的情况下,我真的无法为您提供更多关于如何分块数据的指导,但是我建议您不要设置像“一次一天”这样的任意限制,而不考虑您的情况的典型模式。例如,在某些应用程序中,一次限制为一天可能最终会成为一次一条记录,但在其他应用程序中,一次可能是数千条记录。您应该与其他程序员讨论,看看在这种情况下典型的模式是什么。 ;)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-30
  • 1970-01-01
  • 2022-10-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多