如何为LINQ to SQL编写Upsert?

时间:2009-03-12 02:57:30

标签: linq linq-to-sql

所以我想为LINQ to SQL编写一个通用的Upsert函数,我在构思它的过程中遇到了一些麻烦。我希望它可以像这样工作:

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);

我假设有必要让这个来组成一个查询来判断该项目是否已经在数据库中,但我现在还不知道如何处理它。

2 个答案:

答案 0 :(得分:8)

我做了类似的事情,但采用了不同的方法。每个实体都实现IEntityIEntity的一个属性是对象是新对象还是现有对象的状态。然后,我为每个实体实现,例如:

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附加,也要确保你的对象有时间戳。

答案 1 :(得分:1)

简答:

抓住这个:EntityExtensionMethods.cs

<强>解释

要在LINQ-to-SQL中执行UPSERT而不先查询记录,您可以执行以下操作。它仍将命中数据库一次以检查记录是否存在但不会记录:

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