Rob*_*dez 8 c# entity-framework entity-framework-core
如何在不声明外键的情况下设置导航属性?
我有两个表(segment
和category
),它们可以使用 2 列(来源/目的地)进行连接,但它们不符合外键约束,因为它们可以具有另一个表中不存在的值(因此它与 null 或不为空)
部分
Id Date OriginId DestinationId
---------------------------------------
1 2020-01-10 1 2
2 2020-01-18 2 1
2 2020-02-05 1 3
4 2020-04-11 3 3
Run Code Online (Sandbox Code Playgroud)
类别
Id OriginId DestinationId Category
-----------------------------------------
1 1 2 Primary
2 2 1 Secondary
2 2 3 Primary
Run Code Online (Sandbox Code Playgroud)
我想知道每个Segment
类别。但并非每个细分市场都存在,Category
因此某些细分市场没有类别。
此 SQL 有效:
SELECT
s.*, c.name
FROM
Segment s
LEFT OUTER JOIN
Category c ON c.originId = s.originId AND c.destinationId = s.destinationId
Run Code Online (Sandbox Code Playgroud)
这个 LINQ 也有效:
from s in Segment
join c in Category on new { s.OriginId, s.DestinationId } equals new { c.OriginId, c.DestinationId } into grouping
from c in grouping.DefaultIfEmpty()
select new { s, c };
Run Code Online (Sandbox Code Playgroud)
但是要使用导航,我得到的最接近的是:我分别在每个类上添加了一个属性,并使用 fluent api 在上下文中设置关系
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Segment>()
.HasOne(segment => segment.Category)
.WithMany(category => category.Segments)
.HasForeignKey(segment => new { segment.OriginId, segment.DestinationId })
.HasPrincipalKey(category => new { category.OriginId, category.DestinationId })
.IsRequired(false);
}
// then I can do
var query = Segments.Include(o => o.Category).ToList();
Run Code Online (Sandbox Code Playgroud)
这用于检索数据库中已存在的记录。
但是要向类别中不存在 originId 和 destinationId 的 Segment 插入新记录,它会抱怨 originId 和 destinationId 不符合外键约束。没关系,因为它们不存在于另一个表中。
它们实际上不是外键,只是用于加入它们的列,但我不知道如何使用导航属性 + Fluent Api 来设置它。
小智 6
抱歉——这个聚会有点晚了。
我刚刚经历了类似的练习。你可以试试这个:
modelBuilder.Entity<Category>()
.HasMany(category => category.Segments)
.WithOne(segment => segment.Category)
.HasPrincipalKey(category => new { category.OriginId, category.DestinationId })
.IsRequired(false)
.OnDelete(DeleteBehavior.NoAction);
Run Code Online (Sandbox Code Playgroud)
小智 4
第一的:
\n [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "EF1001:Internal EF Core API usage.", Justification = "<\xe6\x8c\x82\xe8\xb5\xb7>")]\n public class MigrationsModelDifferWithoutForeignKey : MigrationsModelDiffer\n {\n public MigrationsModelDifferWithoutForeignKey\n ([NotNull] IRelationalTypeMappingSource typeMappingSource,\n [NotNull] IMigrationsAnnotationProvider migrationsAnnotations,\n [NotNull] IChangeDetector changeDetector,\n [NotNull] IUpdateAdapterFactory updateAdapterFactory,\n [NotNull] CommandBatchPreparerDependencies commandBatchPreparerDependencies)\n : base(typeMappingSource, migrationsAnnotations, changeDetector, updateAdapterFactory, commandBatchPreparerDependencies)\n {\n }\n\n public override IReadOnlyList<MigrationOperation> GetDifferences(IModel source, IModel target)\n {\n var operations = base.GetDifferences(source, target)\n .Where(op => !(op is AddForeignKeyOperation))\n .Where(op => !(op is DropForeignKeyOperation))\n .ToList();\n\n foreach (var operation in operations.OfType<CreateTableOperation>())\n operation.ForeignKeys?.Clear();\n\n return operations;\n }\n }\n
Run Code Online (Sandbox Code Playgroud)\n如何使用:
\nservices.AddDbContext<MyDbContext>(options =>\n{\n options.UseSqlServer(Default);\n options.ReplaceService<IMigrationsModelDiffer, MigrationsModelDifferWithoutForeignKey>();\n});\n
Run Code Online (Sandbox Code Playgroud)\n