Pure POCO entity update problem in repository pattern

后端 未结 3 969
迷失自我
迷失自我 2020-12-09 13:48

I have a problem in my UserRepository in which I want to update a user. I dont want certain fields updated, such as password, unless specified. For example, When I pass the

相关标签:
3条回答
  • 2020-12-09 14:24

    You should use view models. View models are classes which are specifically tailored to the needs of a view and contain only the properties needed by this given view. So your controller action should look like this:

    [HttpPost]
    public ActionResult Update(UserViewModel model) { ... }
    

    instead of:

    [HttpPost]
    public ActionResult Update(User model) { ... }
    

    Inside the controller action you could map between the view model and the model. AutoMapper is a great tool that could simplify this task.

    You should really be very careful and never expose your models like this. Always use view models to and from a view. Just imagine if there was an IsAdministrator boolean property on your model.

    0 讨论(0)
  • 2020-12-09 14:33

    Can you do this?

    public User Save(User user)
        {
            if (user.UserId > 0)
            {
                User dbUser = context.Users.FirstOrDefault(u => u.UserId == user.UserId);
                //What do I do here?
                dbUser.Email = user.Email
                user = dbUser;
            }
            else
            {
                context.Users.AddObject(user);
            }
            context.SaveChanges();
            return user;
        }
    
    0 讨论(0)
  • 2020-12-09 14:36

    Detached POCO scenario (you will not load user from DB before update):

    You can selectively say which properties must be updated:

    public User Save(User user)     
    {         
        if (user.UserId == 0)         
        {             
            context.Users.AddObject(user);         
        }
        else
        {
            context.Users.Attach(user);
            ObjectStateEntry entry = context.ObjectStateManager.GetObjectStateEntry(user);
            entry.SetModifiedProperty("Email");
        }
    
        context.SaveChanges();         
        return user;     
    }
    

    You can also create two overloads of you Save method. First will update whole object, second will update only explicitly selected properties:

    public User Save(User user)     
    {         
        if (user.UserId == 0)         
        {             
            context.Users.AddObject(user);         
        }
        else
        {
            context.Users.Attach(user);
            context.ObjectStateManager.ChangeObjectState(user, EntityState.Modified);        
        }
    
        context.SaveChanges();         
        return user;     
    }
    
    public User Save(User user, IEnumerable<Expression<Func<User, object>>> properties)     
    {         
        if (user.UserId == 0)         
        {             
            context.Users.AddObject(user);         
        }
        else
        {
            context.Users.Attach(user);
            ObjectStateEntry entry = context.ObjectStateManager.GetObjectStateEntry(user);
            foreach(var selector in properties)
            {
                string propertyName = PropertyToString(selector.Body);
                entry.SetModifiedProperty(propertyName);
            }
        }
    
        context.SaveChanges();         
        return user;     
    }
    
    // Doesn't work for navigation properties!
    private static string PropertyToString(Expression selector)
    {
        if (selector.NodeType == ExpressionType.MemberAccess)
        {
            return ((selector as MemberExpression).Member as PropertyInfo).Name;
        }
    
        throw new InvalidOperationException();
    }
    

    You will call the second overload this way:

    userRepository.Save(user, new List<Expression<Func<User, object>>> 
        { 
            u => u.Email 
        });
    

    Attached scenario (you will load user from DB before update):

    You can modify your Save method to accept delegate so that you can control how update will be performed:

    public User Save(User user, Action<User, User> updateStrategy)                                
    {                                  
        if (user.UserId > 0)                                  
        {
            User dbUser = context.Users.FirstOrDefault(u => u.UserId == user.UserId);
            updateStrategy(dbUser, user);                                                                        
        }        
        else
        {                          
            // New object - all properties should be saved
            context.Users.AddObject(user);
        }
    
        context.SaveChanges();                                  
        return user;                              
    }  
    

    You will call the method this way:

    var user = GetUpdatedUserFromSomewhere();
    repository.Save(user, (dbUser, mergedUser) => 
        {
            dbUser.Email = mergedUser.Email;
        });
    

    Anyway, despite of my examples you should definitely think about Darin's post and special ModelViews for updating.

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