最近在开发记录感想功能的时候用到了1对1的数据关系,具体情况是这样的,有这样两个1对1的类型

public class Item
{
public int Id { get; set; }
public string Title { get; set; }
public Note Note { get; set; }
} public class Note
{
public int Id { get; set; }
public string Content { get; set; }
public int ItemId { get; set; }
public Item Item { get; set; }
public bool Deleted { get; set; }
}

它们的1对1关系配置如下:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Note>(e =>
{
e.HasOne(x => x.Item).WithOne(x => x.Note).HasForeignKey<Note>(x => x.ItemId);
e.HasQueryFilter(x => !x.Deleted);
});
}

Note是软删除的,这里配置了一个QueryFilter

然后我们用dotnet-ef命令构建数据库,生成的脚本如下:

IF OBJECT_ID(N'[__EFMigrationsHistory]') IS NULL
BEGIN
CREATE TABLE [__EFMigrationsHistory] (
[MigrationId] nvarchar(150) NOT NULL,
[ProductVersion] nvarchar(32) NOT NULL,
CONSTRAINT [PK___EFMigrationsHistory] PRIMARY KEY ([MigrationId])
);
END; GO CREATE TABLE [Items] (
[Id] int NOT NULL IDENTITY,
[Title] nvarchar(max) NULL,
CONSTRAINT [PK_Items] PRIMARY KEY ([Id])
); GO CREATE TABLE [Notes] (
[Id] int NOT NULL IDENTITY,
[Content] nvarchar(max) NULL,
[ItemId] int NOT NULL,
[Deleted] bit NOT NULL,
CONSTRAINT [PK_Notes] PRIMARY KEY ([Id]),
CONSTRAINT [FK_Notes_Items_ItemId] FOREIGN KEY ([ItemId]) REFERENCES [Items] ([Id]) ON DELETE CASCADE
); GO CREATE UNIQUE INDEX [IX_Notes_ItemId] ON [Notes] ([ItemId]); GO INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
VALUES (N'20190813141425_InitEntities', N'2.2.6-servicing-10079'); GO

再造一条数据,方便测试

USE [demo]
GO INSERT INTO [dbo].[Items]
([Title])
VALUES
('a')
GO

不出意外的话,这个ItemId会是1

业务代码如下:

[ApiController]
[Route("[controller]")]
public class NoteController : ControllerBase
{
private readonly DemoContext _db;
public NoteController(DemoContext db)
{
_db = db;
} [HttpGet]
public IEnumerable<Note> Get()
{
return _db.Notes.ToList();
} [HttpPost]
public void Post()
{
var item = _db.Items.Include(x => x.Note).FirstOrDefault(x => x.Id == 1);
if (item != null)
{
item.AddNote(DateTime.Now.ToString("F"));
_db.SaveChanges();
}
} [HttpDelete]
public void Delete()
{
var item = _db.Items.Include(x => x.Note).FirstOrDefault(x => x.Id == 1);
if (item != null)
{
item.DeleteNote();
_db.SaveChanges();
}
}
}

就是对Id==1Item新增/修改/删除Note

有这样一个很简单的场景,用户先新增了Note,然后删除Note,再想新增Note,这时候你就会发现数据库报错了:Note违反了唯一性约束。

由于Note是软删除的,所有当再次新增Note的时候就会出现重复的ItemId

解决这个问题的思路也很简单,只需要把这个外键的唯一性约束更改为过滤掉Deleted的数据进行约束。

更改关系配置

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Note>(e =>
{
e.HasOne(x => x.Item).WithOne(x => x.Note).HasForeignKey<Note>(x =x.ItemId);
e.HasQueryFilter(x => !x.Deleted);
e.HasIndex(x => x.ItemId).IsUnique().HasFilter($"[{nameof(Note.Deleted)}]=0");
});
}

给这个ItemId的唯一性约束加一个条件e.HasIndex(x => x.ItemId).IsUnique().HasFilter($"[{nameof(Note.Deleted)}]=0");

再用dotnet-ef命令生成的数据库更新脚本,如下:

DROP INDEX [IX_Notes_ItemId] ON [Notes];

GO

CREATE UNIQUE INDEX [IX_Notes_ItemId] ON [Notes] ([ItemId]) WHERE [Deleted]=0;

GO

INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
VALUES (N'20190813144240_FilterIndex', N'2.2.6-servicing-10079'); GO

用有条件的INDEX替换了原先的INDEX

现在再次执行先前的业务,新增,删除,再次新增就正常了。

完整代码github

EF Core 中处理 1对1 关系的更多相关文章

  1. [小技巧]EF Core中如何获取上下文中操作过的实体

    原文地址:https://www.cnblogs.com/lwqlun/p/10576443.html 作者:Lamond Lu 源代码:https://github.com/lamondlu/EFC ...

  2. 9.翻译系列:EF 6以及EF Core中的数据注解特性(EF 6 Code-First系列)

    原文地址:http://www.entityframeworktutorial.net/code-first/dataannotation-in-code-first.aspx EF 6 Code-F ...

  3. 9.4 翻译系列:EF 6以及 EF Core中的NotMapped特性(EF 6 Code-First系列)

    原文链接:http://www.entityframeworktutorial.net/code-first/notmapped-dataannotations-attribute-in-code-f ...

  4. 12.翻译系列:EF 6 中配置一对多的关系【EF 6 Code-First系列】

    原文链接:https://www.entityframeworktutorial.net/code-first/configure-one-to-many-relationship-in-code-f ...

  5. EF Core 中多次从数据库查询实体数据,DbContext跟踪实体的情况

    使用EF Core时,如果多次从数据库中查询一个表的同一行数据,DbContext中跟踪(track)的实体到底有几个呢?我们下面就分情况讨论下. 数据库 首先我们的数据库中有一个Person表,其建 ...

  6. EF Core中如何正确地设置两张表之间的关联关系

    数据库 假设现在我们在SQL Server数据库中有下面两张表: Person表,代表的是一个人: CREATE TABLE [dbo].[Person]( ,) NOT NULL, ) NULL, ...

  7. EF Core 2.1 支持数据库一对一关系

    在使用EF Core和设计数据库的时候,通常一对多.多对多关系使用得比较多,但是一对一关系使用得就比较少了.最近我发现实际上EF Core很好地支持了数据库的一对一关系. 数据库 我们先来看看SQL ...

  8. EF Core中如何通过实体集合属性删除从表的数据

    假设在数据库中有两个表:Person表和Book表,Person和Book是一对多关系 Person表数据: Book表数据: 可以看到数据库Book表中所有的数据都属于Person表中"F ...

  9. 项目开发中的一些注意事项以及技巧总结 基于Repository模式设计项目架构—你可以参考的项目架构设计 Asp.Net Core中使用RSA加密 EF Core中的多对多映射如何实现? asp.net core下的如何给网站做安全设置 获取服务端https证书 Js异常捕获

    项目开发中的一些注意事项以及技巧总结   1.jquery采用ajax向后端请求时,MVC框架并不能返回View的数据,也就是一般我们使用View().PartialView()等,只能返回json以 ...

随机推荐

  1. Spring Boot通过ImportBeanDefinitionRegistrar动态注入Bean

    在阅读Spring Boot源码时,看到Spring Boot中大量使用ImportBeanDefinitionRegistrar来实现Bean的动态注入.它是Spring中一个强大的扩展接口.本篇文 ...

  2. cbv请求分析

    CBV源码分析 DRF中中所有视图都是基于CBV形式完成, 所以分析其cbv源码, 了解drf的基本请求流程就比较有必要了. urls.py """下面是一个通用的url ...

  3. html代码/如何做到有横线无竖线的表格/或横线有颜色/竖线没颜色

    改变它的css样式,table{ border-collapse:collapse;}table tr td{ border-bottom:1px solid #dedede;}

  4. OSI案例详解+ARP+DNS

    案例,淘宝买东西 DNS 域名的作用其实就是相对ip地址来说更好记忆和识别,DNS就是根据域名解析出ip 一次请求,DNS解析的过程 1.在浏览器输入一个域名,操作系统首先会检查自己的本地hosts文 ...

  5. Linux安装python环境脚本

    自动安装python环境的脚本 1.首先判断是不是root用户 2.判断是否安装 3.是否下载成功(网络可能有问题) 4.是否解压成功(文件下载可能缺少) 5.安装配置python环境 # codin ...

  6. 【SSL1194】最优乘车

    题面: 正文: 把每个边用链式前向星存起来,边权为\(1\),就可以愉♂快♂地最短路了

  7. python中字典数据类型常用操作

    创建字典 字典是另一种可变容器模型,且可存储任意类型对象. 字典的每个键值(key=>value)对用冒号(:)分割,每个对之间用逗号(,)分割,整个字典包括在花括号({})中 ,格式如下所示: ...

  8. 移动开发在路上-- IOS移动开发系列 网络交互四(1)

    最近一段时间上班忙的没日没夜的,不是披星戴月,就是头天早上出门,第二天早上回家...一直没出处时间来更新博客,码农之苦,说了都是泪,废话不多说,直接本主题,经过之前三篇的讲述,ios开发的东西大家或多 ...

  9. [TimLinux] Python 迭代器(iterator)和生成器(generator)

    1. 可迭代对象 from collection import Iterable class Iterable(metaclass=ABCMeta): ... def __iter__(self): ...

  10. [TimLinux] Python学习内容框架

    以下内容主体来自<Python学习手册第四版>,大致整理出的方向 1. 第一部分:使用入门 介绍Python语法之前,先对Python的的各个方面进行一个比较宽广的介绍,包含对Python ...