How would you write an Upsert for LINQ to SQL?

前端 未结 2 758
时光取名叫无心
时光取名叫无心 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:22

    I do something similar, but with a different approach. Every entity implements IEntity. One of the properties of IEntity is a state if the object is new or existing. I then implement that for each entity, like:

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

    Then, a generic Upsert could be (on a generic repository type class):

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

    If your attaching from another datacontext, also make sure you have a timestamp on your objects.

    0 讨论(0)
  • 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<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);
            }
        }
    }
    

    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

    0 讨论(0)
提交回复
热议问题