【问题标题】:Linq InsertAllOnSubmit if not exist如果不存在,则 Linq InsertAllOnSubmit
【发布时间】:2015-10-23 14:32:57
【问题描述】:

如果不存在我正在插入记录:

using(MyDataContext db = new MyDataContext())
{
    for(int i=0 i<Items.Length;i++)
    {
        if(!db.Items.Any(item=> item.name == Items[i].name)
            db.Items.InsertOnSubmit(Items[i]);
    }
    db.SubmitChanges();
}

我知道我也可以逐个检查这些项目并将不存在的项目插入另一个列表,然后最后使用InsertAllOnSubmit() 将它们全部插入,但这也不能解决我的问题检查数千个项目,我不想执行未经处理的查询来检查这些项目。有没有办法以仅插入非现有项目的方式使用 InsertAllOnSibmit?

编辑:我没有使用 Linq 的义务,如果有人知道通过存储过程或任何其他方式来做这件事的更好方法,我真的很感激你分享。

【问题讨论】:

    标签: c# linq tsql


    【解决方案1】:

    一个查询找出需要插入的内容,然后在进行一些内存操作后,您可以InsertAllOnSubmit

    using(MyDataContext db = new MyDataContext())
    {
        var itemNames = Items.Select(i=>i.name).ToList();
        var itemNamesInDb = db.Items.Where(i=>itemNames.Contains(i.Name)).Select(i=>i.Name).ToList();
        var itemNamesNotInDb = new HashSet<string>(itemNames.Except(itemNamesInDb));
        var itemsNotInDb = Items.Where(i=>itemNamesNotInDb.Contains(i.name)).ToList();
    
        db.Items.InsertAllOnSubmit(itemsNotInDb);
        db.SubmitChanges();
    }
    

    【讨论】:

    • i=&gt;itemNames.Contains(i) 奇怪的谓词。
    • 可能会更糟,如果 db 有 500 万个项目要全部选择到内存中执行 where 方法来获取 itemNamesInDb。
    • 如果我正确地解释了你的反对意见,@vitalygolub,Where 不在本地内存中执行;它将被 EF 翻译成生成的 sql 中的 WHERE...IN 子句,在数据库服务器上执行。
    • 如果我的表中有很多记录,会影响您的解决方案性能吗?
    • 在 db.Items 中有很多记录不应该过多地惩罚查询,尤其是在 Name 上有索引的情况下。但是您应该知道,它可以处理的 Items 集合中的项目数量存在限制(EF 限制)。如果您遇到该限制,您会想要拆分这些查询(创建一个单独的函数,给定 DbContext 和 Items 列表,获取 itemsNotInDb;拆分原始 Items 列表,在每个子上调用该函数-list,并在 InsertAllOnSubmit 之前合并结果)。
    【解决方案2】:

    不必那么复杂。名称似乎是主键。只需找出数据库中的哪些名称在您要插入的项目集中。然后插入它们,排除已存在的。

    var excludedNames = new HashSet<string>(
        from i in db.Items
        where Items.Select(item => item.Name).Contains(i.Name)
        select i.Name
    );
    db.Items.InsertAllOnSubmit(
        from i in Items
        where !excludedNames.Contains(i.Name)
        select i
    );
    db.SubmitChanges();
    

    【讨论】:

      【解决方案3】:

      您可以尝试以下操作: 请注意,这种并发性可能是一个问题,我看到这个工作正常的唯一方法是使用存储过程和阻塞策略,但如果想用 L2SQL 来做......

      //1st db hit, to get the existing records, existing count never superior to toInsert count, 
      //so it should not comsume a lot of memory
      var existing = db.Items.Where(p => toInsert.Contains(p.Name)).ToList();
      
      foreach (var input in toInsert)
      {
          //fast in memory lookup
          if (existing.FirstOrDefault(p => p.Name== input) == null)
          {
             //insert the element
          }
      }
      
      //2nd db hit - submit to DB
      

      【讨论】:

      • 你能告诉我如何用存储过程来做吗?
      • 没有义务使用 linq。如果您有更好的建议使用存储过程。我真的很感激。
      • 我可以,但是这超出了问题的范围,因为您将其标记为 linq,使用 SQL 或 Transact-SQL 重新标记,并且您可以通过存储过程获得专家。 =)
      • 我编辑了我的问题,但它并不完全在范围内;)
      • 按原样,您正在对要插入的每个项目执行查询。 existingIQueryable&lt;Item&gt;。您至少必须将其转换为列表。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-15
      • 1970-01-01
      • 2019-08-20
      • 1970-01-01
      • 2015-03-31
      • 2013-05-26
      相关资源
      最近更新 更多