创建/更新方案中的部分模型验证

时间:2012-02-13 01:07:47

标签: asp.net-mvc-3 validation

我有一个由EF从现有数据库生成的用户模型:

public class User
{
 public int Id { get;set; }
 public string Name { get;set; }
 public string Password { get;set; }
 public DateTime Created { get;set; }
 public DateTime LastModified { get;set; }
}

我一直在创建UserModel并应用数据注释来添加验证。我使用AutoMapper在User和UserModel之间进行转换。

我现在正尝试使用以下业务规则创建视图:

  • 创建用户时,Created和LastModified字段不应出现在UI中,但应在模型保存到存储库之前设置它们;
  • 在编辑用户时,Created和LastModified字段不应出现在UI中,但Created字段应保持不变,并且应在模型保存到存储库之前更新LastModified字段;
  • 编辑用户时,如果未触摸密码字段,则不更改模型中的密码字段;如果密码字段包含值,则应使用该值来更新模型。

那么如何通过尽可能少的代码重复来实现这一目标呢?例如,我应该有一个EditUserModel和一个CreateUserModel,从一个基本的UserModel继承,它有两个共同的字段(Id,Name,Password)?任何一个模型是否都有对Created / LastModified的引用?特别是,我如何处理密码更改要求?

2 个答案:

答案 0 :(得分:2)

这就是我通常处理这种情况的方式。对于视图模型,我只使用一个,EditUserModel,因为它实际上没有回报维护3个类只有1-2个属性是不同的,在我看来,视图模型并不重要,我是只是务实。在您的情况下,EditUserModel应该看起来像这样

public class EditUserModel
{
   public int Id {get;set} //used when modifying user
   public string Name {get;set;}
   public string Password {get;set;}
   public string ConfirmPassword {get;set;} //this is optionally
}

这个模型被传递给控制器​​,我个人使用DDD方法(作为一种心态),让我们说一个看起来像这样的域模型(它基于我实际使用的代码)

public class Password
{
   public Password(string value,string salt)
   {
      //you can apply basic validation rules if you want\\

      Hash=(salt+value).Sha256(); //a variant of hashing and an extension method
      Salt=salt;
   }
   public string Hash {get;private set;}
   public string Salt {get;private set;} 
}

    public class Member
{
   public Member(string name, Password pwd)
   {
       Name=name;
       Password=pwd;
       Created= DateTime.Now;
    }

    public Member(int id,DateTime created,string name,Password p)
    {
        Id = id;
        Created = created;
        _name = name;
        _password = p;
    }
   public int Id { get; set; } 
    private string _name;

    public string Name
    {
        get { return _name; }
        set
        {
            LastModified = DateTime.Now;
            _name = value;
        }
    }

    private Password _password;
    public Password Password
    {
        get { return _password; }
        set
        {
            _password = value;
            LastModified = DateTime.Now;
        }
    }

    public DateTime Created {get;private set;}
   public DateTime LastModified {get;private set;}
}

第一次创建用户时

var user= new Member(model.Name,new Password(model.Password,"a salt"));
repository.Save(user);

更新时

var user= repository.GetUser(int id);
user.Name=model.Name;
if (!string.IsNullOrEmpty(model.Password)) user.Password= new Password(model.Password,"a salt");
repository.Save(user);

在存储库中(请注意,我不具备使用EF的经验,因此可以优化此代码)

public void Save(Member user)
{
using (var dc = new DbContext())
    {

 if (user.Id==0)
  {
     //do insert, all this can be handled via automapper
    var u= new User();
    u.Name=user.Name;
    u.Password=user.Password.Hash;
    u.Created=user.Created;
    u.LastModified=user.LastModifed;  
    dc.Users.Add(u);
    dc.SaveChanges();
    user.Id=u.Id;

  }
else  
 {
    //do edit
    var u= dc.Users.First(d=>d.Id==user.Id);
    //map values \\

    dc.Users.SaveChanges();// EF should detect if something was changed and save only changes
  }
}
}

  public Member GetUser(int id)
   {
     //get User from DbContext \\

      var m= new Member(id,user.Created,user.Name,new Password(user.Password,"my salt")); 
      return m;


    }

这是仓促的代码,一切都可以改进,但让我告诉为什么这种“复杂”的方法。首先,我们在层的响应性之间有一个更明确的区别,视图模型处理所有显示和将数据发送回控制器所需的内容,并且域模型不与持久性模型(EF模型)混合。您可以很容易地更改密码哈希techqnique,并且在真正发生更改的地方处理创建/最后修改的内容。当您开始添加更多功能时,此设计将使您更轻松。

主要目标是清晰度和可维护性,并依赖于使用,您可以采取另一种方法,例如通过命令更新用户(向存储库发送命令以更改名称或密码)

好的,我现在完成了:)

答案 1 :(得分:1)

对于前两个问题,Created和LastModified字段应在模型中定义为readonly或根本不映射,您可以在数据库中使用INSERT和UPDATE触发器来设置它们的值。

另一种可能性是在数据上下文中使用override the SaveChanges方法并在实体上调用相应的方法,以便在插入或更新之前设置这些属性的值。

  

编辑用户时,如果未触及密码字段,则表示   模型中的密码字段不会更改;如果是密码字段   包含一个值,用于更新模型。

您可以尝试在映射层处理此要求。