|
我习惯性使用OData,它的$expand与层级查询非常好用,这个功能非常依赖于数据库的导航属性,也就是外键结构。最近想着把一个单体的系统拆分为多个小系统,首先需要处理外键依赖的问题。
多个服务各自有各自的数据库,数据库层面并不互通,也就无法使用外键约束。
我使用EF Core来描述数据库的结构,有两个实体类如下:- public class AD_Insect_Info
- {
- [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id {get;set;}
- public string Name { get; set; }
- public virtual List<AD_Insect_Datum> Results { get; set; }
- }
- public class AD_Insect_Datum
- {
- [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id { get; set; }
- public virtual AD_Insect_Info AttachDevice { get; set; }
- [NotMapped]
- public override string AttachId => AttachDevice.AttachDeviceId;
- }
复制代码 可以看到他们之间有一个导航属性,实现一个一对多的关系。如果系统是新系统,大可直接进行拆解,想怎么弄就怎么弄。但是对于已经有较多数据的现有系统,最好使用渐进拆分的方式。
思路:通过多次,每次只修改一点,维持对旧有系统的兼容性,并在完全拆分前保持系统高度可用,破坏性最小。
指定外键
跨数据库的访问,数据完整性不能由数据库通过外键实现,我们需要在应用层实现自己的逻辑。由于外键不存在,我们需要在数据表中有表示对外引用的字段。而对于导航属性而言,外键已经由EF Core自动建立,为了防止数据结构变化导致的问题,我们可以明确指定外键。
查询数据表结构
EF Core有默认的外键命名规则,通常是字段名+外键的字段名,对于本例,则是AttachDeviceId,保险起见,我们可以查询数据库获得外键列名称。
显式指定外键名称
导航属性的外键名字不是那么直观,我们可以使用EF Core的ForeignKey特性。- public class AD_Insect_Datum : AttachDataBase
- {
- [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public int Id { get; set; }
- [ForeignKey("AttachDeviceId")]
- public virtual AD_Insect_Info AttachDevice { get; set; }
- public int AttachDeviceId { get; set; }
- }
复制代码 这样,这个对象Id就被显式指定了。
查询替代
前一步指定了外键,但是实际上并没有对EF Core表做任何更改,原来的程序可以正常运行,我们需要在这个基础上继续改造,将使用导航属性的地方修改一下。- public virtual async Task<IActionResult> Data(string key)
- {
- return Ok(_context.AD_Insect_Data.Where(w => w.AttachDevice.Name == key));
- }
复制代码 修改为:- public virtual async Task<IActionResult> Data(string key)
- {
- var instances = _context.AD_Insect_Infos.Where(w=>w.Name == key).Select(w=> w.Id).ToList();
- return Ok(_context.AD_Insect_Data.Where(w => instances.Contains(w.AttachDeviceId)));
- }
复制代码 这样就避免使用导航属性依赖。
我这里也没有使用连表查询方法,因为将来需要拆分。
删除外键
由于不再使用导航属性,可以安全地删除外键。注意,可能需要补充一些业务逻辑以确保数据库中的数据完整性。
拆分DbContext
将来DbContext将不再拥有AD_Insect_Infos,因此我们需要进一步拆分。- public virtual async Task<IActionResult> Data(string key)
- {
- IEnumerable<int> instances = Foreign.GetList(key);
- return Ok(_context.AD_Insect_Data.Where(w => instances.Contains(w.AttachDeviceId)));
- }
- public class Foreign
- {
- public IEnumerable<int> GetList(string key)
- {
- return _context.AD_Insect_Infos.Where(w=>w.Name == key).Select(w=> w.Id).ToList();
- }
- }
复制代码上面代码表示大致的思路,请自行处理注入依赖等问题。
然后就是熟悉的动作了,将Foreign类作为一个数据服务,只要返回的类型是IEnumerable就可以了,数据的来源可以是本身,也可以是外部的服务。
来源:https://www.cnblogs.com/podolski/archive/2023/04/27/17357900.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作! |
|