How to update only one field using Entity Framework?

前端 未结 16 1466
后悔当初
后悔当初 2020-11-22 09:09

Here\'s the table

Users

UserId
UserName
Password
EmailAddress

and the code..



        
相关标签:
16条回答
  • 2020-11-22 09:46

    I know this is an old thread but I was also looking for a similar solution and decided to go with the solution @Doku-so provided. I'm commenting to answer the question asked by @Imran Rizvi , I followed @Doku-so link that shows a similar implementation. @Imran Rizvi's question was that he was getting an error using the provided solution 'Cannot convert Lambda expression to Type 'Expression> [] ' because it is not a delegate type'. I wanted to offer a small modification I made to @Doku-so's solution that fixes this error in case anyone else comes across this post and decides to use @Doku-so's solution.

    The issue is the second argument in the Update method,

    public int Update(T entity, Expression<Func<T, object>>[] properties). 
    

    To call this method using the syntax provided...

    Update(Model, d=>d.Name, d=>d.SecondProperty, d=>d.AndSoOn); 
    

    You must add the 'params' keyword in front of the second arugment as so.

    public int Update(T entity, params Expression<Func<T, object>>[] properties)
    

    or if you don't want to change the method signature then to call the Update method you need to add the 'new' keyword, specify the size of the array, then finally use the collection object initializer syntax for each property to update as seen below.

    Update(Model, new Expression<Func<T, object>>[3] { d=>d.Name }, { d=>d.SecondProperty }, { d=>d.AndSoOn });
    

    In @Doku-so's example he is specifying an array of Expressions so you must pass the properties to update in an array, because of the array you must also specify the size of the array. To avoid this you could also change the expression argument to use IEnumerable instead of an array.

    Here is my implementation of @Doku-so's solution.

    public int Update<TEntity>(LcmsEntities dataContext, DbEntityEntry<TEntity> entityEntry, params Expression<Func<TEntity, object>>[] properties)
         where TEntity: class
        {
            entityEntry.State = System.Data.Entity.EntityState.Unchanged;
    
            properties.ToList()
                .ForEach((property) =>
                {
                    var propertyName = string.Empty;
                    var bodyExpression = property.Body;
                    if (bodyExpression.NodeType == ExpressionType.Convert
                        && bodyExpression is UnaryExpression)
                    {
                        Expression operand = ((UnaryExpression)property.Body).Operand;
                        propertyName = ((MemberExpression)operand).Member.Name;
                    }
                    else
                    {
                        propertyName = System.Web.Mvc.ExpressionHelper.GetExpressionText(property);
                    }
    
                    entityEntry.Property(propertyName).IsModified = true;
                });
    
            dataContext.Configuration.ValidateOnSaveEnabled = false;
    
            return dataContext.SaveChanges();
        }
    

    Usage:

    this.Update<Contact>(context, context.Entry(modifiedContact), c => c.Active, c => c.ContactTypeId);
    

    @Doku-so provided a cool approach using generic's, I used the concept to solve my issue but you just can't use @Doku-so's solution as is and in both this post and the linked post no one answered the usage error questions.

    0 讨论(0)
  • 2020-11-22 09:47

    In EntityFramework Core 2.x there is no need for Attach:

     // get a tracked entity
     var entity = context.User.Find(userId);
     entity.someProp = someValue;
     // other property changes might come here
     context.SaveChanges();
    

    Tried this in SQL Server and profiling it:

    exec sp_executesql N'SET NOCOUNT ON;
    UPDATE [User] SET [someProp] = @p0
    WHERE [UserId] = @p1;
    SELECT @@ROWCOUNT;
    
    ',N'@p1 int,@p0 bit',@p1=1223424,@p0=1
    

    Find ensures that already loaded entities do not trigger a SELECT and also automatically attaches the entity if needed (from the docs):

        ///     Finds an entity with the given primary key values. If an entity with the given primary key values
        ///     is being tracked by the context, then it is returned immediately without making a request to the
        ///     database. Otherwise, a query is made to the database for an entity with the given primary key values
        ///     and this entity, if found, is attached to the context and returned. If no entity is found, then
        ///     null is returned.
    
    0 讨论(0)
  • 2020-11-22 09:49

    i'm using this:

    entity:

    public class Thing 
    {
        [Key]
        public int Id { get; set; }
        public string Info { get; set; }
        public string OtherStuff { get; set; }
    }
    

    dbcontext:

    public class MyDataContext : DbContext
    {
        public DbSet<Thing > Things { get; set; }
    }
    

    accessor code:

    MyDataContext ctx = new MyDataContext();
    
    // FIRST create a blank object
    Thing thing = ctx.Things.Create();
    
    // SECOND set the ID
    thing.Id = id;
    
    // THIRD attach the thing (id is not marked as modified)
    db.Things.Attach(thing); 
    
    // FOURTH set the fields you want updated.
    thing.OtherStuff = "only want this field updated.";
    
    // FIFTH save that thing
    db.SaveChanges();
    
    0 讨论(0)
  • 2020-11-22 09:51

    Ladislav's answer updated to use DbContext (introduced in EF 4.1):

    public void ChangePassword(int userId, string password)
    {
        var user = new User() { Id = userId, Password = password };
        using (var db = new MyEfContextName())
        {
            db.Users.Attach(user);
            db.Entry(user).Property(x => x.Password).IsModified = true;
            db.SaveChanges();
        }
    }
    
    0 讨论(0)
提交回复
热议问题