LINQ to SQL insert-if-non-exist

cor*_*ore 11 c# linq-to-sql

我想知道如果在表中不存在记录,是否有更简单的方法来插入记录.我还在尝试构建我的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;
}
Run Code Online (Sandbox Code Playgroud)

Mar*_*ade 14

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);
Run Code Online (Sandbox Code Playgroud)

  • 这不会造成竞争条件吗?在Any()之后和SubmitChanges()调用之前,还有什么东西可以改变数据库吗? (10认同)
  • 如果使用Expression,它将被转换为SQL并在数据库中执行.裸Func可以将整个表加载到内存中.任何委托类型都可以用于Expression <T>. (4认同)

Mic*_*pat 12

正如其他人所指出的,if (!Any()) { InsertOnSubmit(); }解决方案都有竞争条件.如果你走这条路线,当你打电话时SubmitChanges,你必须考虑到a)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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在......如果你必须在与其他数据库更新的批处理事务中执行插入,事情会变得更加复杂.


Pan*_*nos 5

同意marxidad的回答,但请参阅注释1.

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

注2:该InsertIfNotExists<TEntity>方法是一种非常通用的方法,适用于任何场景.如果您只想区分从数据库加载的实体与从代码创建的实体,您可以使用OnLoadedEntity类的partial方法,如下所示:

public partial class MyEntity
{
    public bool IsLoaded { get; private set; }
    partial void OnLoaded()
    {
        IsLoaded = true;
    }
}
Run Code Online (Sandbox Code Playgroud)

鉴于(和注释1),然后InsertIfNotExists功能减少到以下:

if (!record.IsLoaded)
    db.InsertOnSubmit(record);
Run Code Online (Sandbox Code Playgroud)

  • 一个更好的设计可能是该方法被称为InsertOnSubmitIfNotExists()并省略table.Context.SubmitChanges() (3认同)