Подтвердить что ты не робот

Как вы могли бы написать Upsert для LINQ to SQL?

Итак, я бы хотел написать обобщенную функцию Upsert для LINQ to SQL, и у меня возникли проблемы с концепцией, как это сделать. Я бы хотел, чтобы он работал примерно так:

var db = new DataContext();
db.Customers.UpsertOnSubmit(customer);

Таким образом, это должно было быть общим в некотором роде, и я предполагаю, и метод расширения в таблице. Я смог донести это до определения первичного ключа базовой таблицы:

var context = source.Context;
var table = context.Mapping.GetTable(source.GetType());
var primaryMember = table.RowType.DataMembers.SingleOrDefault(m => m.IsPrimaryKey);

Я предполагаю, что это будет необходимо, чтобы это составляло запрос, чтобы определить, находится ли элемент в базе данных уже или нет, но я не знаю, что с ним делать в данный момент.

4b9b3361

Ответ 1

Я делаю что-то подобное, но с другим подходом. Каждый объект реализует IEntity. Одним из свойств IEntity является состояние, если объект является новым или существующим. Затем я реализую это для каждой сущности, например:

public EntityState EntityState
{
    get
    {
        if (_Id > 0)
            return EntityState.Exisiting;
        else
            return EntityState.New;
    }
}

Тогда общий Upsert может быть (в классе типа общего репозитория):

public virtual void Upsert<Ta>(Ta entity)
    where Ta: class
{
    if (!(entity is IEntity))
        throw new Exception("T must be of type IEntity");

    if (((IEntity)entity).EntityState == EntityState.Exisiting)
        GetTable<Ta>().Attach(entity, true);
    else
        GetTable<Ta>().InsertOnSubmit(entity);
}

private System.Data.Linq.Table<Ta> GetTable<Ta>()
    where Ta: class
{
    return _dataContext.Context.GetTable<Ta>();
}

Если вы прикрепляетесь к другому datacontext, также убедитесь, что у вас есть метка времени для ваших объектов.

Ответ 2

Короткий ответ:

Захватите это: EntityExtensionMethods.cs

Объяснение

Чтобы выполнить UPSERT в LINQ-to-SQL без предварительного запроса записей, вы можете сделать следующее. Он по-прежнему будет бить db один раз, чтобы проверить, существует ли запись, но не потянет запись:

var blob = new Blob { Id = "some id", Value = "some value" }; // Id is primary key (PK)

if (dbContext.Blobs.Contains(blob)) // if blob exists by PK then update
{
    // This will update all columns that are not set in 'original' object. For
    // this to work, Blob has to have UpdateCheck=Never for all properties except
    // for primary keys. This will update the record without querying it first.
    dbContext.Blobs.Attach(blob, original: new Blob { Id = blob.Id });
}
else // insert
{
    dbContext.Blobs.InsertOnSubmit(blob);
}
dbContext.Blobs.SubmitChanges();

Метод расширения

Я придумал для него следующий метод расширения.

public static class EntityExtensionMethods
{
    public static void InsertOrUpdateOnSubmit<TEntity>(this Table<TEntity> table, TEntity entity, TEntity original = null)
        where TEntity : class, new()
    {
        if (table.Contains(entity)) // if entity exists by PK then update
        {
            if (original == null)
            {
                // Create original object with only primary keys set
                original = new TEntity();
                var entityType = typeof(TEntity);
                var dataMembers = table.Context.Mapping.GetMetaType(entityType).DataMembers;
                foreach (var member in dataMembers.Where(m => m.IsPrimaryKey))
                {
                    var propValue = entityType.GetProperty(member.Name).GetValue(entity, null);
                    entityType.InvokeMember(member.Name, BindingFlags.SetProperty, Type.DefaultBinder,
                        original, new[] {propValue});
                }
            }

            // This will update all columns that are not set in 'original' object. For
            // this to work, entity has to have UpdateCheck=Never for all properties except
            // for primary keys. This will update the record without querying it first.
            table.Attach(entity, original);
        }
        else // insert
        {
            table.InsertOnSubmit(entity);
        }
    }
}

Используйте его, как показано ниже:

var blob = new Blob { Id = "some id", Value = "some value" }; // Id is primary key (PK)
dbContext.Blobs.InsertOrUpdateOnSubmit(blob);
dbContext.Blobs.SubmitChanges();

Я добавил выше метод расширения с большим количеством материалов к этой сути: EntityExtensionMethods.cs