我有一个由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之间进行转换。
我现在正尝试使用以下业务规则创建视图:
那么如何通过尽可能少的代码重复来实现这一目标呢?例如,我应该有一个EditUserModel和一个CreateUserModel,从一个基本的UserModel继承,它有两个共同的字段(Id,Name,Password)?任何一个模型是否都有对Created / LastModified的引用?特别是,我如何处理密码更改要求?
答案 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方法并在实体上调用相应的方法,以便在插入或更新之前设置这些属性的值。
编辑用户时,如果未触及密码字段,则表示 模型中的密码字段不会更改;如果是密码字段 包含一个值,用于更新模型。
您可以尝试在映射层处理此要求。