存储库模式中的纯POCO实体更新问题

Sha*_*ean 4 asp.net-mvc entity-framework repository-pattern

我的UserRepository中有一个问题,我想在其中更新用户.除非指定,否则我不希望更新某些字段,例如密码.例如,当我将用户从视图传递到服务器到存储库时,它会向用户发送空密码或空密码字符串.这个null被写入数据库(我不想要).

我该如何处理这样的情况?

public class User
{
    public int UserId { get; set; }

    public string Email { get; set; }
    public string Password { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

知识库

    public User Save(User user)
    {
        if (user.UserId > 0)
        {
            User dbUser = context.Users.FirstOrDefault(u => u.UserId == user.UserId);
            //What do I do here?
        }
        context.Users.AddObject(user);
        context.SaveChanges();
        return user;
    }
Run Code Online (Sandbox Code Playgroud)

让我们说在这种情况下,我的视图只允许我更改Email,因此唯一被发送回Save()方法的是:user.UserIduser.Emailwhile user.Password是null.在我的情况下,数据库抛出错误,因为密码应该可以为空.

Lad*_*nka 10

分离的POCO场景(在更新之前不会从DB加载用户):

您可以有选择地说出必须更新哪些属性:

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;     
}
Run Code Online (Sandbox Code Playgroud)

您还可以创建两个Save方法的重载.首先将更新整个对象,第二个将仅更新明确选择的属性:

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();
}
Run Code Online (Sandbox Code Playgroud)

你将以这种方式调用第二个重载:

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

附加方案(您将在更新前从DB加载用户):

您可以修改Save方法以接受委托,以便您可以控制如何执行更新:

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;                              
}  
Run Code Online (Sandbox Code Playgroud)

您将以这种方式调用方法:

var user = GetUpdatedUserFromSomewhere();
repository.Save(user, (dbUser, mergedUser) => 
    {
        dbUser.Email = mergedUser.Email;
    });
Run Code Online (Sandbox Code Playgroud)

无论如何,尽管有我的例子,你一定要考虑Darin的帖子和特殊的ModelViews进行更新.