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

发布于 2024-10-11 19:38:25 字数 880 浏览 6 评论 0原文

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

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

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

    public string Email { get; set; }
    public string Password { get; set; }
}

存储库

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

可以说,在这种情况下,我的视图允许我仅更改电子邮件,因此唯一发送回Save()方法是:user.UserIduser.Email,而user.Password为null。就我而言,数据库会抛出错误,因为密码应该可为空。

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 User from the view, to the service to the repository, it sends up the user with a null or empty password string. This null gets written to the database (which I dont want).

How do I handle a situation like this?

Domain

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

    public string Email { get; set; }
    public string Password { get; set; }
}

Repository

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

Lets say in this case, my view allows me to change only Email, so the only thing that gets sent back to the Save() method are: user.UserId and user.Email while user.Password is null. In my case, the database throws error because Password should be nullable.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

梦里人 2024-10-18 19:38:25

分离的 POCO 场景(更新前不会从数据库加载用户):

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

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

您还可以创建 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();
}

您将这样调用第二个重载:

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

附加场景(您将在更新之前从数据库加载用户):

您可以将 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;                              
}  

您将这样调用该方法:

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

无论如何,尽管有我的示例,您绝对应该考虑 Darin 的帖子和用于更新的特殊 ModelView。

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.

能怎样 2024-10-18 19:38:25

您应该使用视图模型。视图模型是专门根据视图的需要定制的类,并且仅包含该给定视图所需的属性。因此,您的控制器操作应该如下所示:

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

而不是:

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

在控制器操作内部,您可以在视图模型和模型之间进行映射。 AutoMapper 是一个可以简化此任务的出色工具。

你真的应该非常小心,永远不要像这样暴露你的模型。始终在视图中使用视图模型。试想一下,如果您的模型上有一个 IsAdministrator 布尔属性。

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.

絕版丫頭 2024-10-18 19:38:25

你能做到吗?

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

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;
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文