EF Core Table 每个层次结构 - 是否可以跟踪特定实体的集合?
EF Core Table Per Hierarchy - Is it possible to keep track of collections for specific Entity?
假设我将为 class 实现一个 Table 每个层次结构,我将在其中存储由鉴别器(~ 5 种类型)区分的这种类型的子class。
一些 subclasses 将有自己的 ICollections 而有些则不会,所以这不会在 superclass 中指定。我目前只能获取直接存储在 table 中的数据,但无法获取此子 class 的集合(集合长度将为 0)
当我从数据库中获取这个特定的 subclass(带有特定的鉴别器)对象时,关于如何填写这个列表有什么想法吗?
这是一个完整的示例控制台项目,演示了这种方法:
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace IssueConsoleTemplate
{
public class IceCream
{
public int IceCreamId { get; set; }
public string Name { get; set; }
}
public class IceCreamAsDrink : IceCream
{
public string DrinkName { get; set; }
}
public class IceCreamWithToppings : IceCream
{
public ICollection<Topping> Toppings { get; set; } = new HashSet<Topping>();
}
public class Topping
{
public int ToppingId { get; set; }
public string Name { get; set; }
public int IceCreamWithToppingsIceCreamId { get; set; } // <-- use this exact
// name or use the
// Fluent API
public IceCreamWithToppings IceCreamWithToppings { get; set; }
}
public class Context : DbContext
{
public DbSet<IceCream> IceCreams { get; set; }
public DbSet<IceCreamAsDrink> IceCreamsAsDrink { get; set; }
public DbSet<IceCreamWithToppings> IceCreamsWithToppings { get; set; }
public DbSet<Topping> Toppings { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer(
@"Data Source=.\MSSQL14;Integrated Security=SSPI;Initial Catalog=So63087805")
.UseLoggerFactory(
LoggerFactory.Create(
b => b
.AddConsole()
.AddFilter(level => level >= LogLevel.Information)))
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<IceCreamWithToppings>()
.HasMany(e => e.Toppings)
.WithOne(e => e.IceCreamWithToppings)
.HasForeignKey(e => e.IceCreamWithToppingsIceCreamId);
modelBuilder.Entity<IceCream>().HasData(
new IceCream
{
IceCreamId = 1,
Name = "Basic Vanilla"
});
modelBuilder.Entity<IceCreamAsDrink>().HasData(
new IceCreamAsDrink
{
IceCreamId = 2,
Name = "Vanilla Ice Coffee",
DrinkName = "Coffee"
});
modelBuilder.Entity<IceCreamWithToppings>().HasData(
new IceCreamWithToppings
{
IceCreamId = 3,
Name = "Vanilla With Sprinkles"
});
modelBuilder.Entity<Topping>().HasData(
new Topping
{
ToppingId = 1,
Name = "Chocolate Sprinkles",
IceCreamWithToppingsIceCreamId = 3
},
new Topping
{
ToppingId = 2,
Name = "Whipped Cream",
IceCreamWithToppingsIceCreamId = 3
});
}
}
internal static class Program
{
private static void Main()
{
using var context = new Context();
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
var allIceCreams = context.IceCreams
.OrderBy(i => i.IceCreamId)
.ToList();
var iceCreamsAsDrink = context.IceCreamsAsDrink
.ToList();
var iceCreamsWithToppings = context.IceCreamsWithToppings
.Include(i => i.Toppings)
.ToList();
Debug.Assert(allIceCreams.Count == 3);
Debug.Assert(iceCreamsAsDrink.Count == 1);
Debug.Assert(iceCreamsWithToppings.Count == 1);
Debug.Assert(iceCreamsWithToppings[0].Toppings.Count == 2);
}
}
}
如果要使用Topping
和IceCreamWithToppings
之间的外键约定,则需要将外键命名为<DerivedType><PrimaryKeyOnBaseType>
,所以这里的IceCreamWithToppingsIceCreamId
案例.
或者,只需使用 Fluent API:
定义关系
public class Topping
{
public int ToppingId { get; set; }
public string Name { get; set; }
public int MyForeignKeyToIceCream { get; set; } // <-- non-convention name
public IceCreamWithToppings IceCreamWithToppings { get; set; }
}
public class Context : DbContext
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<IceCreamWithToppings>()
.HasMany(e => e.Toppings)
.WithOne(e => e.IceCreamWithToppings)
.HasForeignKey(e => e.MyForeignKeyToIceCream);
}
}
假设我将为 class 实现一个 Table 每个层次结构,我将在其中存储由鉴别器(~ 5 种类型)区分的这种类型的子class。 一些 subclasses 将有自己的 ICollections 而有些则不会,所以这不会在 superclass 中指定。我目前只能获取直接存储在 table 中的数据,但无法获取此子 class 的集合(集合长度将为 0) 当我从数据库中获取这个特定的 subclass(带有特定的鉴别器)对象时,关于如何填写这个列表有什么想法吗?
这是一个完整的示例控制台项目,演示了这种方法:
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace IssueConsoleTemplate
{
public class IceCream
{
public int IceCreamId { get; set; }
public string Name { get; set; }
}
public class IceCreamAsDrink : IceCream
{
public string DrinkName { get; set; }
}
public class IceCreamWithToppings : IceCream
{
public ICollection<Topping> Toppings { get; set; } = new HashSet<Topping>();
}
public class Topping
{
public int ToppingId { get; set; }
public string Name { get; set; }
public int IceCreamWithToppingsIceCreamId { get; set; } // <-- use this exact
// name or use the
// Fluent API
public IceCreamWithToppings IceCreamWithToppings { get; set; }
}
public class Context : DbContext
{
public DbSet<IceCream> IceCreams { get; set; }
public DbSet<IceCreamAsDrink> IceCreamsAsDrink { get; set; }
public DbSet<IceCreamWithToppings> IceCreamsWithToppings { get; set; }
public DbSet<Topping> Toppings { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer(
@"Data Source=.\MSSQL14;Integrated Security=SSPI;Initial Catalog=So63087805")
.UseLoggerFactory(
LoggerFactory.Create(
b => b
.AddConsole()
.AddFilter(level => level >= LogLevel.Information)))
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<IceCreamWithToppings>()
.HasMany(e => e.Toppings)
.WithOne(e => e.IceCreamWithToppings)
.HasForeignKey(e => e.IceCreamWithToppingsIceCreamId);
modelBuilder.Entity<IceCream>().HasData(
new IceCream
{
IceCreamId = 1,
Name = "Basic Vanilla"
});
modelBuilder.Entity<IceCreamAsDrink>().HasData(
new IceCreamAsDrink
{
IceCreamId = 2,
Name = "Vanilla Ice Coffee",
DrinkName = "Coffee"
});
modelBuilder.Entity<IceCreamWithToppings>().HasData(
new IceCreamWithToppings
{
IceCreamId = 3,
Name = "Vanilla With Sprinkles"
});
modelBuilder.Entity<Topping>().HasData(
new Topping
{
ToppingId = 1,
Name = "Chocolate Sprinkles",
IceCreamWithToppingsIceCreamId = 3
},
new Topping
{
ToppingId = 2,
Name = "Whipped Cream",
IceCreamWithToppingsIceCreamId = 3
});
}
}
internal static class Program
{
private static void Main()
{
using var context = new Context();
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
var allIceCreams = context.IceCreams
.OrderBy(i => i.IceCreamId)
.ToList();
var iceCreamsAsDrink = context.IceCreamsAsDrink
.ToList();
var iceCreamsWithToppings = context.IceCreamsWithToppings
.Include(i => i.Toppings)
.ToList();
Debug.Assert(allIceCreams.Count == 3);
Debug.Assert(iceCreamsAsDrink.Count == 1);
Debug.Assert(iceCreamsWithToppings.Count == 1);
Debug.Assert(iceCreamsWithToppings[0].Toppings.Count == 2);
}
}
}
如果要使用Topping
和IceCreamWithToppings
之间的外键约定,则需要将外键命名为<DerivedType><PrimaryKeyOnBaseType>
,所以这里的IceCreamWithToppingsIceCreamId
案例.
或者,只需使用 Fluent API:
定义关系public class Topping
{
public int ToppingId { get; set; }
public string Name { get; set; }
public int MyForeignKeyToIceCream { get; set; } // <-- non-convention name
public IceCreamWithToppings IceCreamWithToppings { get; set; }
}
public class Context : DbContext
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<IceCreamWithToppings>()
.HasMany(e => e.Toppings)
.WithOne(e => e.IceCreamWithToppings)
.HasForeignKey(e => e.MyForeignKeyToIceCream);
}
}