How would you write an Upsert for LINQ to SQL?

前端 未结 2 763
时光取名叫无心
时光取名叫无心 2020-12-09 04:09

So I\'d like to write a generic Upsert function for LINQ to SQL and I\'m having some trouble conceptualizing how to do it. I\'d like it to work something like this:

2条回答
  •  陌清茗
    陌清茗 (楼主)
    2020-12-09 04:34

    Short Answer:

    Grab this: EntityExtensionMethods.cs

    Explanation

    To do UPSERT in LINQ-to-SQL without querying the records first, you can do the following. It will still hit the db once to check if record exists but will not pull the record:

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

    Extension Method

    I came up with the following extension method for it.

    public static class EntityExtensionMethods
    {
        public static void InsertOrUpdateOnSubmit(this Table 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);
            }
        }
    }
    

    Use it like below:

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

    I added above extension method with more stuff to this gist: EntityExtensionMethods.cs

提交回复
热议问题