简体   繁体   English

没有外键的 EF Core 关系

[英]EF Core relationship without foreign key

How to setup a navigation property without declaring a foreign key?如何在不声明外键的情况下设置导航属性?

I have two tables ( segment and category ), they can join using 2 columns (origin/destination) but they don't meet the foreign key constraint because they can have value that doesn't exist in the other table (so its not about null or not null)我有两个表( segmentcategory ),它们可以使用 2 列(origin/destination)连接,但它们不满足外键约束,因为它们可以具有另一个表中不存在的值(所以它不是关于null 或不为空)

Segment部分

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

Category类别

Id  OriginId   DestinationId  Category
-----------------------------------------
1      1            2         Primary
2      2            1         Secondary
2      2            3         Primary

I want to know every Segment category.我想知道每个Segment类别。 But not every segment exists in Category so some segments won't have a category.但并非每个细分都存在于Category中,因此某些细分不会有类别。

This SQL works:这个 SQL 工作:

SELECT 
    s.*, c.name
FROM 
    Segment s
LEFT OUTER JOIN 
    Category c ON c.originId = s.originId AND c.destinationId = s.destinationId

This LINQ also works:这个 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 };

But to use navigation, closest I got is this: I added a property on each class respectively and setup the relationship in context using fluent api但是要使用导航,我得到的最接近的是:我分别在每个 class 上添加了一个属性,并使用流利的 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();

This works to retrieve records that already exists in the database.这可以检索数据库中已经存在的记录。

But to insert a new record to Segment where originId and destinationId do not exists in Category it complains about originId and destinationId not meeting the foreign key constraint.但是要在 Category 中不存在 originId 和 destinationId 的 Segment 中插入一条新记录,它会抱怨 originId 和 destinationId 不满足外键约束。 And it's ok because they don't exist in the other table.没关系,因为它们不存在于另一个表中。

They actually aren't foreign keys, just a column to use for joining them, but I don't khow to set this using a navigation property + Fluent Api.它们实际上不是外键,只是用于连接它们的列,但我不知道如何使用导航属性 + Fluent Api 来设置它。

First:第一的:

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "EF1001:Internal EF Core API usage.", Justification = "<挂起>")]
    public class MigrationsModelDifferWithoutForeignKey : MigrationsModelDiffer
    {
        public MigrationsModelDifferWithoutForeignKey
            ([NotNull] IRelationalTypeMappingSource typeMappingSource,
            [NotNull] IMigrationsAnnotationProvider migrationsAnnotations,
            [NotNull] IChangeDetector changeDetector,
            [NotNull] IUpdateAdapterFactory updateAdapterFactory,
            [NotNull] CommandBatchPreparerDependencies commandBatchPreparerDependencies)
            : base(typeMappingSource, migrationsAnnotations, changeDetector, updateAdapterFactory, commandBatchPreparerDependencies)
        {
        }

        public override IReadOnlyList<MigrationOperation> GetDifferences(IModel source, IModel target)
        {
            var operations = base.GetDifferences(source, target)
                .Where(op => !(op is AddForeignKeyOperation))
                .Where(op => !(op is DropForeignKeyOperation))
                .ToList();

            foreach (var operation in operations.OfType<CreateTableOperation>())
                operation.ForeignKeys?.Clear();

            return operations;
        }
    }

How to use:如何使用:

services.AddDbContext<MyDbContext>(options =>
{
    options.UseSqlServer(Default);
    options.ReplaceService<IMigrationsModelDiffer, MigrationsModelDifferWithoutForeignKey>();
});

Sorry - a bit late to this party.抱歉 - 这个聚会有点晚了。

I just went through a similar excercise.我刚刚经历了类似的练习。 You could try this:你可以试试这个:

modelBuilder.Entity<Category>()
    .HasMany(category => category.Segments)
    .WithOne(segment => segment.Category)
    .HasPrincipalKey(category => new { category.OriginId, category.DestinationId })
    .IsRequired(false)
    .OnDelete(DeleteBehavior.NoAction);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM