public void Delete()
{
testDbContext.Order.Remove(testDbContext.Order.Find(1006));
testDbContext.SaveChanges();
}
这种方式会执行两次sql语句,第一次是将实体查询出来,第二次是执行删除语句
通过上下文查询出来的实体都会被标记一个状态Unchanged,再执行Remove方法时会标记状态为deleted,调用savechenges方法则会执行删除的sql语句。
public void Delete1()
{
var order = new Order { OrderId = 1007 };
testDbContext.Entry(order).State = EntityState.Deleted;
testDbContext.SaveChanges();
}
public void Delete2()
{
var order = new Order { OrderId = 1007 };
testDbContext.Order.Remove(order);
testDbContext.SaveChanges();
}
上面两种方式都会执行一条sql语句,在new 实体类中,主键属性被定义就行了。
关系表删除
仅当使用 EF Core 删除主体且将依赖实体加载到内存中(即对于跟踪的依赖项)时才应用 EF Core 模型中配置的删除行为。 需要在数据库中设置相应的级联行为以确保未由上下文跟踪的数据已应用必要的操作。 如果使用 EF Core 创建数据库,将为你设置此级联行为。
可选关系(就是外键值可以设置成null)
对于可选关系(可以为 null 的外键),可以保存 null 外键值,从而产生以下影响:
行为名称 | 对内存中的依赖项/子项的影响 | 对数据库中的依赖项/子项的影响 |
---|---|---|
Cascade | 删除实体 | 删除实体 |
ClientSetNull(默认) | 外键属性设置为 null | 无 |
SetNull | 外键属性设置为 null | 外键属性设置为 null |
Restrict | 无 | 无 |
必选关系(就是外键值不可以设置成null)
对于必选关系(不可为 null 的外键),_不可以_保存 null 外键值,从而产生以下影响:
行为名称 | 对内存中的依赖项/子项的影响 | 对数据库中的依赖项/子项的影响 |
---|---|---|
Cascade(默认) | 删除实体 | 删除实体 |
ClientSetNull | SaveChanges 引发异常 | 无 |
SetNull | 引发 SaveChanges | 引发 SaveChanges |
Restrict | 无 | 无 |
在上表中,“无” 可能会造成约束冲突。 例如,如果已删除主体/子实体,但不执行任何操作来更改依赖项/子项的外键,则由于发生外键约束冲突,数据库将可能会引发 SaveChanges。
高级别:
- 如果实体在没有父项时不能存在,且希望 EF 负责自动删除子项,则使用“Cascade” 。
- 在没有父项时不能存在的实体通常使用必选关系,其中“Cascade” 是默认值。
- 如果实体可能有或可能没有父项,且希望 EF 负责为你将外键变为 null,则使用“ClientSetNull”
- 在没有父项时可以存在的实体通常使用可选关系,其中“ClientSetNull” 是默认值。
- 如果希望数据库即使在未加载子实体时也尝试将 null 值传播到子外键,则使用“SetNull” 。 但是,请注意,数据库必须支持此操作,并且如此配置数据库可能会导致其他限制,实际上这通常会使此选项不适用。 这就是SetNull不是默认值的原因。
- 如果不希望 EF Core 始终自动删除实体或自动将外键变为 null,则使用“Restrict” 。 请注意,这要求使用代码手动同步子实体及其外键值,否则将引发约束异常。
不管EF CORE默认是什么,我们都可以手动修改默认值,如我的例子中,因为没有强加外键不可为空,所以默认的级联删除,会删除父类,而将子类的外键设置成null
也可以自定义属性
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Order>().HasOne(f => f.User).WithMany(a => a.Orders).OnDelete(DeleteBehavior.Cascade);
}
这样删除父类的时候会同时将子类删除掉
public void DeleteRelation()
{
testDbContext.User.Remove(testDbContext.User.Include(a=>a.Orders).FirstOrDefault(t=>t.UserId==1007));
testDbContext.SaveChanges();
}
TestEFCore> Executed DbCommand (39ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
TestEFCore> SELECT TOP(1) [a].[UserId], [a].[DefaultCurrencyCode], [a].[FirstName], [a].[LastName], [a].[SecurityLevel]
TestEFCore> FROM [User] AS [a]
TestEFCore> WHERE [a].[UserId] = 1008
TestEFCore> ORDER BY [a].[UserId]
TestEFCore>
TestEFCore> Executed DbCommand (4ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
TestEFCore> SELECT [a.Orders].[OrderId], [a.Orders].[Address], [a.Orders].[City], [a.Orders].[Country], [a.Orders].[Customer], [a.Orders].[OrderDate], [a.Orders].[Timestamp], [a.Orders].[UserId]
TestEFCore> FROM [Order] AS [a.Orders]
TestEFCore> INNER JOIN (
TestEFCore> SELECT TOP(1) [a0].[UserId]
TestEFCore> FROM [User] AS [a0]
TestEFCore> WHERE [a0].[UserId] = 1008
TestEFCore> ORDER BY [a0].[UserId]
TestEFCore> ) AS [t] ON [a.Orders].[UserId] = [t].[UserId]
TestEFCore> ORDER BY [t].[UserId]
TestEFCore>
TestEFCore> Executed DbCommand (1ms) [Parameters=[@p0='?' (DbType = Int32), @p1='?' (Size = 8) (DbType = Binary)], CommandType='Text', CommandTimeout='30']
TestEFCore> SET NOCOUNT ON;
TestEFCore> DELETE FROM [Order]
TestEFCore> WHERE [OrderId] = @p0 AND [Timestamp] = @p1;
TestEFCore> SELECT @@ROWCOUNT;
TestEFCore>
TestEFCore> Executed DbCommand (0ms) [Parameters=[@p2='?' (DbType = Int32)], CommandType='Text', CommandTimeout='30']
TestEFCore> SET NOCOUNT ON;
TestEFCore> DELETE FROM [User]
TestEFCore> WHERE [UserId] = @p2;
TestEFCore> SELECT @@ROWCOUNT;