外键拆分手记

yizhihongxing

我习惯性使用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<int>就可以了,数据的来源可以是本身,也可以是外部的服务。

原文链接:https://www.cnblogs.com/podolski/archive/2023/04/27/17357900.html

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:外键拆分手记 - Python技术站

(0)
上一篇 2023年4月25日
下一篇 2023年4月27日

相关文章

  • C#难点逐个击破(1):ref参数传递

    下面是关于“C#难点逐个击破(1):ref参数传递”的完整攻略: 标题 C# 难点逐个击破(1): ref 参数传递 正文 在 C# 中,方法参数通常是按值传递的,也就是说,传给方法的是参数的一个副本,而不是参数本身。但是,在某些情况下,我们需要传递参数本身,而不是它的副本。这时候,我们可以使用 ref 关键字来实现。 ref 关键字的作用是将参数标记为一个…

    C# 2023年6月7日
    00
  • C#中Span相关的性能优化建议

    标题:C#中Span相关的性能优化建议 简介 Span是C#中新增的一种类型,它能够提升数组和字符串的性能表现。下面将给出几个优化建议,帮助开发者正确使用Span。 优化建议 使用Span替换数组 数组是一种引用类型,存放在堆中,而且会由垃圾回收器回收。这个过程比较耗时,所以使用数组可能会降低程序的性能。使用Span可以很好地解决这个问题。 示例: // 使…

    C# 2023年6月8日
    00
  • C#中String类常用方法汇总

    C#中String类常用方法汇总 在C#编程中,String类是我们经常用到的一个类。它包含了很多有用的方法,可以方便我们进行字符串的处理和操作。下面是常用的String类方法汇总。 1. 字符串的创建和初始化 1.1 创建字符串 我们可以使用以下两种方法来创建字符串: 方法一:使用双引号创建 string str1 = "hello, world…

    C# 2023年5月15日
    00
  • C#中IntPtr类型的具体使用

    当需要动态操作内存时,C# 提供了一个 IntPtr 类型,该类型可以包含一个指针或句柄的值。 在C#中,IntPtr类型被广泛使用,它定义为和平台相关大小的整数,通常是32位或64位整数数据类型。一般来说,IntPtr类型在本机环境下使用。下面是IntPtr类的语法。 public struct System.IntPtr : System.Runtime…

    C# 2023年5月31日
    00
  • C#导出数据到excel如何提升性能

    C#导出数据到Excel的过程中,可能会存在性能问题,尤其是在处理大量数据的情况下。以下是提升性能的攻略: 1. 使用OpenXml SDK 使用OpenXml SDK可以直接操作Excel文件的xml结构,而不需要打开Excel应用程序,这样可以提升处理大量数据的性能。可通过下面的代码将数据写入Excel文件: using (var document = …

    C# 2023年5月31日
    00
  • C# Process调用外部程序的实现

    下面我来给大家详细讲解一下“C# Process调用外部程序的实现”的完整攻略。 什么是Process类 Process类是.NET Framework中一个系统级别的类,它提供了一种机制来与操作系统中运行的进程进行交互。通过使用Process类,我们可以创建、启动、停止和操纵操作系统中的进程,并且可以获取有关进程的信息。在C#中,可以通过引用System.…

    C# 2023年5月31日
    00
  • C#实例化和静态类对象调用对比

    下面是针对“C#实例化和静态类对象调用对比”的完整攻略。 一、实例化对象调用方法 在C#中,我们可以创建一个类的实例,从而调用该类的方法或访问该类的属性。实例化对象可以通过new关键字创建,具体代码如下: MyClass obj = new MyClass(); // 创建MyClass类的实例,存储在obj变量中 obj.MyMethod(); // 调用…

    C# 2023年6月1日
    00
  • Unity实现物体左右移动效果

    Unity是一款流行的游戏开发引擎,它可以实现许多游戏功能包括制作物体左右移动效果。下面将详细讲解Unity实现物体左右移动效果的完整攻略。 实现方式 在Unity中实现物体左右移动的基本方式是通过脚本在Update函数中改变物体的位置。因此,我们需要找到需要移动的对象,创建一个用于移动的脚本,并在脚本的Update函数中修改物体的位置。 1. 创建控制脚本…

    C# 2023年6月3日
    00
合作推广
合作推广
分享本页
返回顶部