C# WinForms MVP-被动视图模式下Model的处理疑问
嘿,刚上手MVP-PV模式时,Model的定位和实例化确实是很容易纠结的点,结合你用原生CRUD(不用EF)的场景,我给你捋清楚这两个问题:
一、Model该包含CRUD逻辑还是仅存属性?
我建议你把CRUD逻辑从Model里抽离出来,让Model只做纯数据属性的载体——也就是常说的「数据传输对象(DTO)」或者「领域实体(无业务逻辑版)」。原因很简单:这符合单一职责原则,让每个类只干一件事。
举个具体的例子:
纯属性的Model
public class User { public int Id { get; set; } public string Username { get; set; } public string Phone { get; set; } }
单独的CRUD处理类(比如叫Repository)
public class UserRepository { private readonly string _connectionString = "你的数据库连接字符串"; public User GetUserById(int id) { // 这里写原生SQL查询逻辑,比如用SqlConnection、SqlCommand User user = null; using (var conn = new SqlConnection(_connectionString)) { conn.Open(); var cmd = new SqlCommand("SELECT Id, Username, Phone FROM Users WHERE Id = @Id", conn); cmd.Parameters.AddWithValue("@Id", id); using (var reader = cmd.ExecuteReader()) { if (reader.Read()) { user = new User { Id = (int)reader["Id"], Username = reader["Username"].ToString(), Phone = reader["Phone"].ToString() }; } } } return user; } public bool AddUser(User user) { // 执行Insert的原生SQL逻辑 using (var conn = new SqlConnection(_connectionString)) { conn.Open(); var cmd = new SqlCommand("INSERT INTO Users (Username, Phone) VALUES (@Username, @Phone)", conn); cmd.Parameters.AddWithValue("@Username", user.Username); cmd.Parameters.AddWithValue("@Phone", user.Phone); return cmd.ExecuteNonQuery() > 0; } } // Update、Delete方法同理,都放在这里 }
如果把CRUD逻辑塞到Model里,会让Model既当「数据容器」又当「数据操作器」,后期维护起来会非常麻烦——比如改数据库连接要动Model,改业务规则也要动Model,耦合度太高了。而抽离到Repository后,Presenter只需要调用Repository的方法,完全不用关心数据是怎么读写的,逻辑清晰很多。
二、Model该在哪里实例化?
分两种场景来看:
- 读取数据时:Model的实例化应该放在Repository的方法里(比如上面
GetUserById方法里new User并赋值),Presenter只需要调用repo.GetUserById()直接拿到现成的Model实例就行,不用自己手动new。 - 创建/更新数据时:推荐在Presenter里实例化Model——View只负责收集用户的输入(比如文本框内容),然后把这些原始值传给Presenter,由Presenter来组装成完整的Model,再交给Repository去保存。这样View的职责更纯粹,只做展示和输入收集,不处理数据组装的逻辑。
另外,关于实例化的位置,还有两个小建议:
- 不要在每个方法里重复实例化相同的依赖(比如Repository),可以在Presenter的构造函数里初始化一次,整个Presenter生命周期内复用;
- 尽量避免在View里直接实例化Model,不然会让View和Model的耦合度变高,违反MVP的分层思想。
给你看个Presenter的示例片段:
public class UserPresenter { private readonly IUserView _userView; private readonly UserRepository _userRepo; // 在Presenter构造函数里初始化依赖 public UserPresenter(IUserView userView) { _userView = userView; _userRepo = new UserRepository(); // 也可以用依赖注入替代手动new,更灵活 } public void LoadUser(int userId) { // 从Repository获取现成的Model实例 User user = _userRepo.GetUserById(userId); if (user != null) { _userView.ShowUsername(user.Username); _userView.ShowPhone(user.Phone); } else { _userView.ShowNotFoundMessage(); } } public void SaveNewUser() { // 从View拿输入,在Presenter里组装Model User newUser = new User { Username = _userView.GetInputUsername(), Phone = _userView.GetInputPhone() }; bool saveSuccess = _userRepo.AddUser(newUser); if (saveSuccess) { _userView.ShowSaveSuccess(); } else { _userView.ShowSaveError(); } } }
这样整个流程的职责就很清晰了:View管展示和输入,Presenter管逻辑调度,Repository管数据读写,Model只负责存数据。
内容的提问来源于stack exchange,提问作者dbay
相关产品推荐
相关产品推荐

