【问题标题】:LINQ to SQL insert-if-non-existentLINQ to SQL 插入 - 如果不存在
【发布时间】:2008-09-19 06:35:08
【问题描述】:

如果表格中不存在记录,我想知道是否有更简单的方法来插入记录。我仍在尝试建立我的 LINQ to SQL 技能。

这就是我所拥有的,但似乎应该有更简单的方法。

public static TEntity InsertIfNotExists<TEntity>
(
    DataContext db,
    Table<TEntity> table,
    Func<TEntity,bool> where,
    TEntity record
)
    where TEntity : class
{
    TEntity existing = table.SingleOrDefault<TEntity>(where);

    if (existing != null)
    {
        return existing; 
    }
    else
    {
        table.InsertOnSubmit(record);

        // Can't use table.Context.SubmitChanges()
        // 'cause it's read-only

        db.SubmitChanges();
    }

    return record;
}

【问题讨论】:

    标签: c# linq-to-sql


    【解决方案1】:
    public static void InsertIfNotExists<TEntity>
                        (this Table<TEntity> table,
                         TEntity entity,
                         Expression<Func<TEntity,bool>> predicate)
        where TEntity : class
    { 
        if (!table.Any(predicate)) 
        {
            table.InsertOnSubmit(record);
            table.Context.SubmitChanges();
        }
     }
    
    
    table.InsertIfNotExists(entity, e=>e.BooleanProperty);
    

    【讨论】:

    • 什么是 e => e.BooleanProperty?实体没有 BooleanProperty 成员。我以前从未见过这个......
    • 嗯,你会注意到在我的代码中,我说过 table.Context.SubmitChanges() 不起作用,因为它是 get-only。显然我犯了一个错误。
    • 最后一个问题(顺便说一句,感谢您的帮助!)。仅使用 Func 的 Expression> 有什么好处?我想起了一些关于“编译表达式”的事情。什么时候应该将 Func 包装在 Expression 中,Action 也可以包装吗?
    • 如果使用Expression,它将被转换为SQL并在数据库中执行。一个裸函数可以将整个表加载到内存中。任何委托类型都可用于表达式.
    • 这不会造成竞争条件吗?在 Any() 之后和 SubmitChanges() 调用之前还有其他东西可以更改数据库吗?
    【解决方案2】:

    正如其他人指出的那样,if (!Any()) { InsertOnSubmit(); } 解决方案都有竞争条件。如果你走这条路,当你调用SubmitChanges 时,你必须考虑到:a)SqlException 可能会引发重复插入,或者 b)表中可能有重复的记录。

    幸运的是,我们可以使用数据库通过强制唯一性来避免竞争条件。以下代码假设表存在主键或唯一约束,以防止插入重复记录。

    using (var db = new DataContext()) {
    
        // Add the new (possibly duplicate) record to the data context here.
    
        try {
            db.SubmitChanges();
        } catch (SqlException ex) {
            const int violationOfPrimaryKeyContraint = 2627;
            const int violationOfUniqueConstraint = 2601;
            var duplicateRecordExceptionNumbers = new [] {
                violationOfPrimaryKeyContraint, violationOfUniqueConstraint
            };
            if (!duplicateRecordExceptionNumbers.Contains(ex.Number)) {
                throw;
            }
        }
    }
    

    现在...如果您必须在批处理事务中与其他数据库更新一起执行插入,事情会变得相当复杂。

    【讨论】:

      【解决方案3】:

      同意marxidad's answer,但见注1。

      注意 1:恕我直言,在辅助方法中调用 db.SubmitChanges() 是不明智的,因为您可能会破坏上下文事务。这意味着,如果您在多个实体的复杂更新过程中调用InsertIfNotExists&lt;TEntity&gt;,您不是一次保存更改,而是分步保存更改。

      注意 2:InsertIfNotExists&lt;TEntity&gt; 方法是一种非常通用的方法,适用于任何场景。如果您只想区分从数据库加载的实体和从代码创建的实体,可以使用 Entity 类的部分方法OnLoaded,如下所示:

      public partial class MyEntity
      {
          public bool IsLoaded { get; private set; }
          partial void OnLoaded()
          {
              IsLoaded = true;
          }
      }
      

      鉴于此(以及注释 1),InsertIfNotExists 功能将简化为以下内容:

      if (!record.IsLoaded)
          db.InsertOnSubmit(record);
      

      【讨论】:

      • 更好的设计可能是方法被称为 InsertOnSubmitIfNotExists() 并省略 table.Context.SubmitChanges()
      【解决方案4】:

      对马克回答的小修改:

      如果您只关心通过其主键检查实体是否存在,Marke 的答案可以这样使用:

      public static void InsertIfNotExists<TEntity>
                          (this Table<TEntity> table
                           , TEntity entity
                          ) where TEntity : class
          {
              if (!table.Contains(entity))
              {
                  table.InsertOnSubmit(entity);
      
              }
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-09-17
        • 2015-03-26
        • 1970-01-01
        相关资源
        最近更新 更多