LINQ to SQL вставки, если не существует - PullRequest
11 голосов
/ 19 сентября 2008

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

Ответы [ 4 ]

14 голосов
/ 19 сентября 2008
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);
12 голосов
/ 26 января 2011

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

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

5 голосов
/ 19 сентября 2008

Согласен с ответом Марксидада , но см. Примечание 1.

Примечание 1: ИМХО, неуместно вызывать 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 голосов
/ 21 января 2010

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

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

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

        }
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...