领域模型中Relationship的编码实现
问题引入
在尝试利用DDD(领域模型驱动开发)的思想来开发新的系统。由于领域模型思考的出发点是按照现实的业务实体来建立,得到一个更接近客观尽量稳定的模型。常常遇到对象之间关系,最常见的就是1对n的关系,比如班级和学生的关系:
在实际设计编码中,对于有关系的2个类如何处理他们的关系呢?
1)双向导航?初始化存在麻烦。
2)单边导航?另外方向的查找会比较麻烦。
很容易得到他们的代码
public class CClase
{
public string Name{get;set;}
public ILazyList<CStudent> students{get;set}
}
public class CStudent
{
public CClase MyClass{get;set;}
public string Name{get;set;}
}
ILazyList<CStudent>将会延迟加载。其实在这个业务模型中,类中的字段应该都是必须的字段,在初始化时应该都能够确定。
在定义构造函数时,有下列几种方式:
理想模式:
CClase(string name,IQueryable<CStudent> stus);
CStudent(int id,string name,CClase c);
在Service Layer或者Repository层有类似下面的代码:
public IQueryable<CClass> GetAllClasses()
{
return from a in _db.Classes select new CClass(a.Name,from b in _db.Students where b.MyClass.Name==a.Name select new CStudent(b.Name,???));
}
???的代码将无法完成。陷入先有鸡还是先有蛋的困境。
折中模式:
一个折中的方案是:
public class CStudent
{
privade string _className;
public CClass MyClass{get{return _Service.GetClassByName(_className);}};
public string Name{get;set;}
public CStudent(string name,string className)
{
Name=name;_className=className;
}
}
在服务层的代码将如下:
public IQueryable<CClass> GetAllClasses()
{
return from a in _db.Classes select new CClass(a.Name,from b in _db.Students where b.MyClass.Name==a.Name select new CStudent(b.Name,a.Name));
}
这样带来的一个问题是领域模型这个层需要了解_Service这个层次,形成依赖。不够干净(不够贫血)
还有一个方案是:
在服务层的代码将如下:
public IQueryable<CClass> GetAllClasses()
{
IQueryable<CClass> classes=from a in _db.Classes select new CClass(a.Name,from b in _db.Students where b.MyClass.Name==a.Name select new CStudent(b.Name,a.Name));
//循环给CStudent中的MyClass赋值,
Return classes;
}
由于循环了classes,这样需要真正执行SQL语句,这样将失去了延迟加载的好处了。
最终的方案:
//-------CStudent.cs---------------------
public class CStudent
{
privade string _className;
public string Name{get;set;}
public CStudent(string name,string className)
{
Name=name;_className=className;
}
}
//---------ClassService.cs-----------------
public class ClassService
{
MyDBContext _db;
public ClassService(MyDBContext dbcotext)
{
_db= dbcotext;
}
public IQueryable<CClass> GetAllClasses()
{
return from a in _db.Classes
let s= GetStudentsByClassName(a.Name)
select new CClass(a.Name,s);
}
public IQueryable<CStudent> GetStudentsByClassName(string name)
{
return from b in _db.Students
where b.MyClass.Name==name
select new CStudent(b.Name,name);
}
public CClass GetClassByName(string name)
{
return from a in GetAllClasses().
where(x=>x.Name==name).
SingleOrDefault();
}
}
如果需要取得某个学生的班级请在使用层调用_Service.GetClassByName(_className)来取得。
也就是说只有单边的直接导航,另一侧采用间接导航。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器