最近在开发记录感想功能的时候用到了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. 以Python为例的Async / Await的编程基础

    来源:Redislabs 作者:Loris Cro 翻译:Kevin (公众号:中间件小哥) 近年来,许多编程语言都在努力改进它们的并发原语.Go 语言有 goroutines,Ruby 有 fibe ...

  2. 鲲鹏云实验-.NET Core 3.0-开始使用

    [摘要] 介绍Ubuntu 18.04环境下.NET Core 3.0的安装配置.初始项目的生成和运行 1. 基础环境 2vCPUs | 4GB | kc1.large.2 Ubuntu 18.04 ...

  3. 【华为云网络技术分享】HTTP重定向HTTPS配置指南

    [摘要] 本文介绍使用华为云弹性负载均衡配置Http重定向到Https的方法. 1. HTTP.HTTPS 头部标识 ELB 对 HTTPS 进行代理,无论是 HTTP 还是 HTTPS 请求,到了  ...

  4. Python之HTTP协议

    HTTP协议,又称超文本传输协议,主要用于浏览器与服务器之间的通信. HTTP 协议的制作者是蒂姆·伯纳斯-李,1991年设计出来的,HTTP 协议设计之前目的是传输网页数据的,现在允许传输任意类型的 ...

  5. AQS系列(二)- ReentrantLock的释放锁

    前言 在AQS系列(一)中我们一起看了ReentrantLock加锁的过程,今天我们看释放锁,看看老Lea那冷峻的思维是如何在代码中笔走龙蛇的. 正文 追踪unlock方法: public void ...

  6. 手撕 JVM 垃圾收集日志

    下图是本篇的写作大纲,将从以下四个方面介绍怎么样处理 JVM 日志. 有准备才能不慌 想要分析日志,首先你得有日志呀,对不对.凡是未雨绸蒙总是没错的.所谓有日志的意思,你要把 JVM 参数配置好,日志 ...

  7. 配置中心入门案例以及出现Could not resolve placeholder 'XXX' in value "${XXX}"的解决方式

    编写配置中心的服务端 创建项目 修改 pom 文件添加 config-server 坐标 <?xml version="1.0" encoding="UTF-8&q ...

  8. [TimLinux] openpyxl 操作Excel

    from openpyxl import Workbook from openpyxl.styles import Color, PatternFill, Font from openpyxl.sty ...

  9. Exponial

    Description Everybody loves big numbers (if you do not, you might want to stop reading at this point ...

  10. vs2017/vs2019 去掉 单击aspx文件预览页面

    初次安装vs2017或者vs2019,新建的项目中,每次单击项目文件时,文件自动打开了 然后 打开 工具->选项->环境->选项卡和窗口->预览选项卡->勾掉" ...