领域建模有两种方式,一是贫血模式,二是充血模型。EFCore对充血模型,已经有了非常好的支持,我们应该通过充血模型的方式来设计实体,将有关个体的业务逻辑封装在实体内。
一、贫血模型:又叫POCO类,类中只有属性或成员变量,没有方法。
1、贫血模型实体类UserAnemic
public class UserAnemic {
public string? UserName { get; set; } public string? Password { get; set; } public int Credit { get; set; } }
2、实现以下几个业务逻辑:①UserAnemic对象必须有名称;②初始积分为10;③判断密码是否正常;④如果登陆成功,积分加5分;如果登陆失败,积分减3分,但如果不够减,则提示积分不足。这些业务逻辑,都属于个体行为,但都暴露在类的外面,且约束力不强,如对象的姓名,很可能会忘记赋值,初始积分,也可能出错。
UserAnemic u1 = new UserAnemic(); u1.UserName = "Test"; u1.Credit = 10; u1.Password ="123456"; string pwd = Console.ReadLine(); if (pwd == u1.Password) { u1.Credit += 5; Console.WriteLine("登陆成功"); } else { if (u1.Credit < 3) { Console.WriteLine("积分不足,无法扣减"); } else { u1.Credit -= 3; Console.WriteLine("登陆失败"); } Console.WriteLine("登陆失败"); }
二、充血模型:类中即有属性、成员变量,也有方法。属于个体的行为,封装在类中,体现“单一职责原则”。当应用中其它地方需要使用这个类时,让使用者无须关心类的内部实现,直接复用类的业务逻辑即可。同时,也体现了业务驱动设计,如下例中的设置密码、检查密码、增加积分、减少积分,业务、产品、开发等各环节沟通的语言均是这些业务逻辑,而不是具体的技术细节。
public class UserRich { //姓名只可以在对象初始化时设置 public string UserName { get; init; } //积分只可以在类的内部修改 public int Credit { get; private set; } //密码外部不可以直接修改 private string? password; //对象初始化,姓名必填,积分初始化为10分 public UserRich(string name) { this.UserName = name; this.Credit = 10; } //设置密码逻辑 public void SetPassword(string password) { if (password.Length<6) { throw new ArgumentException("密码太短了!"); } this.password = password; } //检查密码逻辑 public bool CheckPassword(string password) { return this.password == password; } //减少积分逻辑 public void DeductCredit(int credit) { if (credit<=0) { throw new ArgumentException("额度不能为负值"); } this.Credit -= credit; } //增加积分逻辑 public void AddCredit(int credit) { this.Credit += credit; } }
2、实现业务逻辑
UserRich u1 = new UserRich("functionMC"); u1.SetPassword("123456"); string pwd = Console.ReadLine(); if (u1.CheckPassword(pwd)) { u1.AddCredit(5); Console.WriteLine("登陆成功"); } else { u1.DeductCredit(3); Console.WriteLine("登陆失败"); }
三、EFCore对充血模型的支持:EFCore对充血模型的支持非常好,下例中,列举了EFCore对充血模型的七项支持。
1、充血模型案例
//充血模型User实体类,User.cs public class User { public int Id { get; init; } //EFCore支持①只读或只能在初始化时赋值,Id属性由数据库自动赋值 public DateTime CreatedDateTime { get; init; } //EFCore支持①只读或只能在初始化时赋值 public string UserName { get; private set; } //EFCore支持②只能在类内部修改 public int Credit { get; private set; } //EFCore支持②只能在类内部修改 private string? password; //EFCore支持③私有成员,不能被外部访问,没有对应属性,但需要映射到数据库,在DbContext中设置 private string? remark; public string? Remark { get; } //EFCore支持④只读属性,只能从数据库中读出值,但不能修改属性值 public string? Tag { get; set; } //EFCore支持⑤不需要映射到数据库,在DbContext中设置 private User() //EFCore支持⑥私有无参构造函数,一方面外部无法调用、防止空对象;另一方面,EFCore默认调用,将各个属性与数据库映射 { } public User(string name) //EFCore支持⑦公共有参构造函数,外部调用这个构造函数进行对象的初始化 { this.UserName = name; this.CreatedDateTime = DateTime.Now; this.Credit = 10; } //EFCore支持②只能在类内部修改,外部调用方法修改姓名 public void ChangeUserName(string newName) { this.UserName = newName; } //EFCore支持③私有成员,不能被外部访问,没有对应属性,但需要映射到数据库,在DbContext中设置 public void ChangePassword(string newPassword) { if (newPassword.Length<6) { throw new ArgumentException("密码长度不能低于6位"); } this.password = newPassword; } } //DbContext设置MyDbContext.cs public class MyDbContext:DbContext { public DbSet<User> Users { get; set; } ...... protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<User>(b => { b.ToTable("T_Users"); b.Property("password"); //EFCore支持③私有成员映射到数据库 b.Property(u => u.Remark).HasField("remark"); //EFCore支持④只读属性映射 b.Ignore(u=>u.Tag); //EFCore支持⑤不映射到数据库 }); } }
2、测试代码和结果
using var ctx = new MyDbContext(); //User u1 = new User("functionMC"); //u1.Tag = "TestTag"; //u1.ChangePassword("123456"); //ctx.Users.Add(u1); //ctx.SaveChanges(); var u1 = ctx.Users.FirstOrDefault(); Console.WriteLine(u1); //如要直接打印对象的字符串,将User类改为record。 打印结果: ============================================================================================================ User { Id = 1, CreatedDateTime = 2022/11/25 16:52:14, UserName = functionMC, Credit = 10, Remark = , Tag = }
原文地址:http://www.cnblogs.com/functionMC/p/16925503.html
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,请务用于商业用途!
3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需!
7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员!
8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载
声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性