Pure POCO entity update problem in repository pattern

巧了我就是萌 提交于 2019-11-28 08:49:29

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.

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.

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;
    }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!