Entity Framework 核心与级联删除共享 table
Entity Framework Core shared table with cascade delete
我尝试使用 EF Core(代码优先)创建以下数据库设计
- 实体 "Recipe" 可以有一个类型为 "Resource"
的列表
- 实体 "Shop" 可以有一个 "Resource"
- 实体 "InstructionStep" 可以有一个类型为 "Resource"
的列表
如果我从"Recipe"、"InstructionStep"(集合)或"Shop"(单个-属性)中删除一个资源,那么相应的"Resource" 实体也应删除。 (级联删除)
我已经尝试了几种使用和不使用映射表的方法,但是 none 我的方法是成功的。
另一个想法是在 "Resource" 实体中有一个 属性 "ItemRefId" 来保存 "RecipeId/ShopId/InstructionStepId" 但我没有让它工作...
示例类:
public class Recipe
{
public int RecipeId { get; set; }
public string Title { get; set; }
public ICollection<RecipeResource> Resources { get; set; } = new List<RecipeResource>();
}
public class Shop
{
public int ShopId { get; set; }
public string Title { get; set; }
public Resource Logo { get; set; }
}
public class Resource
{
public int ResourceId { get; set; }
public string Path { get; set; }
public int ItemRefId { get; set; }
}
public class InstructionStep
{
public string InstructionStepId { get; set; }
public string Title { get; set; }
public ICollection<RecipeResource> Resources { get; set; } = new List<RecipeResource>();
}
有什么建议吗?非常感谢。
那不是级联删除。级联删除是指当一个 Recipe 被删除时,所有相关的 Resources 也被删除。
在 EF Core 3 中,您可以为此使用 Owned Entity Types。生成的关系模型与您提议的不同,因为 Recipe_Resource 和 InstructionStep_Resource 将分开 tables,而 Shop.Logo 将存储在商店 table。但这是正确的关系模型。拥有一个 Resource table,其中一些行引用 Recipe 而一些行引用 InstructionStep 是一个坏主意。
这种情况有时称为 "Strong Relationship",其中相关实体的标识依赖于主要实体,并且应该通过将外键列作为主键列在关系模型中实现依赖实体。这样就无法删除 Recipe_Resource 而不删除它。
例如
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Linq;
namespace EfCore3Test
{
public class Recipe
{
public int RecipeId { get; set; }
public string Title { get; set; }
public ICollection<Resource> Resources { get; } = new List<Resource>();
}
public class Shop
{
public int ShopId { get; set; }
public string Title { get; set; }
public Resource Logo { get; set; }
}
public class Resource
{
public int ResourceId { get; set; }
public string Path { get; set; }
public int ItemRefId { get; set; }
}
public class InstructionStep
{
public string InstructionStepId { get; set; }
public string Title { get; set; }
public ICollection<Resource> Resources { get; } = new List<Resource>();
}
public class Db : DbContext
{
public DbSet<Recipe> Recipes { get; set; }
public virtual DbSet<Shop> Shops { get; set; }
public virtual DbSet<InstructionStep> InstructionSteps { get; set; }
private static readonly ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddFilter((category, level) =>
category == DbLoggerCategory.Database.Command.Name
&& level == LogLevel.Information).AddConsole();
});
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseLoggerFactory(loggerFactory)
.UseSqlServer("Server=.;database=EfCore3Test;Integrated Security=true",
o => o.UseRelationalNulls());
base.OnConfiguring(optionsBuilder);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Shop>().OwnsOne(p => p.Logo);
modelBuilder.Entity<InstructionStep>().OwnsMany(p => p.Resources);
modelBuilder.Entity<Recipe>().OwnsMany(p => p.Resources);
}
}
class Program
{
static void Main(string[] args)
{
using var db = new Db();
db.Database.EnsureDeleted();
db.Database.EnsureCreated();
var r = new Recipe();
r.Resources.Add(new Resource() { ItemRefId = 2, Path = "/" });
db.Recipes.Add(r);
db.SaveChanges();
r.Resources.Remove(r.Resources.First());
db.SaveChanges();
var s = new Shop();
s.Logo = new Resource { ItemRefId = 2, Path = "/" };
db.Shops.Add(s);
db.SaveChanges();
s.Logo = null;
db.SaveChanges();
}
}
}
我尝试使用 EF Core(代码优先)创建以下数据库设计
- 实体 "Recipe" 可以有一个类型为 "Resource" 的列表
- 实体 "Shop" 可以有一个 "Resource"
- 实体 "InstructionStep" 可以有一个类型为 "Resource" 的列表
如果我从"Recipe"、"InstructionStep"(集合)或"Shop"(单个-属性)中删除一个资源,那么相应的"Resource" 实体也应删除。 (级联删除)
我已经尝试了几种使用和不使用映射表的方法,但是 none 我的方法是成功的。 另一个想法是在 "Resource" 实体中有一个 属性 "ItemRefId" 来保存 "RecipeId/ShopId/InstructionStepId" 但我没有让它工作...
示例类:
public class Recipe
{
public int RecipeId { get; set; }
public string Title { get; set; }
public ICollection<RecipeResource> Resources { get; set; } = new List<RecipeResource>();
}
public class Shop
{
public int ShopId { get; set; }
public string Title { get; set; }
public Resource Logo { get; set; }
}
public class Resource
{
public int ResourceId { get; set; }
public string Path { get; set; }
public int ItemRefId { get; set; }
}
public class InstructionStep
{
public string InstructionStepId { get; set; }
public string Title { get; set; }
public ICollection<RecipeResource> Resources { get; set; } = new List<RecipeResource>();
}
有什么建议吗?非常感谢。
那不是级联删除。级联删除是指当一个 Recipe 被删除时,所有相关的 Resources 也被删除。
在 EF Core 3 中,您可以为此使用 Owned Entity Types。生成的关系模型与您提议的不同,因为 Recipe_Resource 和 InstructionStep_Resource 将分开 tables,而 Shop.Logo 将存储在商店 table。但这是正确的关系模型。拥有一个 Resource table,其中一些行引用 Recipe 而一些行引用 InstructionStep 是一个坏主意。
这种情况有时称为 "Strong Relationship",其中相关实体的标识依赖于主要实体,并且应该通过将外键列作为主键列在关系模型中实现依赖实体。这样就无法删除 Recipe_Resource 而不删除它。
例如
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Linq;
namespace EfCore3Test
{
public class Recipe
{
public int RecipeId { get; set; }
public string Title { get; set; }
public ICollection<Resource> Resources { get; } = new List<Resource>();
}
public class Shop
{
public int ShopId { get; set; }
public string Title { get; set; }
public Resource Logo { get; set; }
}
public class Resource
{
public int ResourceId { get; set; }
public string Path { get; set; }
public int ItemRefId { get; set; }
}
public class InstructionStep
{
public string InstructionStepId { get; set; }
public string Title { get; set; }
public ICollection<Resource> Resources { get; } = new List<Resource>();
}
public class Db : DbContext
{
public DbSet<Recipe> Recipes { get; set; }
public virtual DbSet<Shop> Shops { get; set; }
public virtual DbSet<InstructionStep> InstructionSteps { get; set; }
private static readonly ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddFilter((category, level) =>
category == DbLoggerCategory.Database.Command.Name
&& level == LogLevel.Information).AddConsole();
});
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseLoggerFactory(loggerFactory)
.UseSqlServer("Server=.;database=EfCore3Test;Integrated Security=true",
o => o.UseRelationalNulls());
base.OnConfiguring(optionsBuilder);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Shop>().OwnsOne(p => p.Logo);
modelBuilder.Entity<InstructionStep>().OwnsMany(p => p.Resources);
modelBuilder.Entity<Recipe>().OwnsMany(p => p.Resources);
}
}
class Program
{
static void Main(string[] args)
{
using var db = new Db();
db.Database.EnsureDeleted();
db.Database.EnsureCreated();
var r = new Recipe();
r.Resources.Add(new Resource() { ItemRefId = 2, Path = "/" });
db.Recipes.Add(r);
db.SaveChanges();
r.Resources.Remove(r.Resources.First());
db.SaveChanges();
var s = new Shop();
s.Logo = new Resource { ItemRefId = 2, Path = "/" };
db.Shops.Add(s);
db.SaveChanges();
s.Logo = null;
db.SaveChanges();
}
}
}