LINQ to SQL insert-if-none-existent

Я хотел бы знать, есть ли более простой способ вставить запись, если она еще не существует в таблице. Я все еще пытаюсь создать свои навыки 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 read-only

        db.SubmitChanges();
    }

    return record;
}

Ответ 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);

Ответ 2

Как отмечали другие, все решения 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;
        }
    }
}

Теперь... ситуация становится более сложной, если вам нужно выполнить вставку в пакетной транзакции с другими обновлениями базы данных.

Ответ 3

Согласитесь с ответом marxidad, но см. примечание 1.

Примечание 1: IMHO, не разумно вызывать db.SubmitChanges() в вспомогательном методе, потому что вы можете разорвать контекстную транзакцию. Это означает, что если вы вызываете InsertIfNotExists<TEntity> в середине сложного обновления нескольких объектов, вы сохраняете изменения не сразу, а в шагах.

Примечание 2: Метод InsertIfNotExists<TEntity> - очень общий метод, который работает для любого сценария. Если вы хотите просто различить сущности, загруженные из базы данных из сущностей, созданных из кода, вы можете использовать частичный метод OnLoaded для класса Entity следующим образом:

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

Учитывая, что (и примечание 1), функциональность InsertIfNotExists сводится к следующему:

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

Ответ 4

Небольшая модификация для ответа Mark:

Если вам нужно только проверить, существует ли сущность по его первичному ключу, ответ Marke можно использовать следующим образом:

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

        }
    }