如何在不声明外键的情况下设置导航属性?
我有两个表(segment
和category
),它们可以使用2列(起源/目标)进行联接,但是它们不满足外键约束,因为它们可以具有不符合条件的值。 t存在于另一个表中(所以它不是大约为null或不为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
类别
Id OriginId DestinationId Category
-----------------------------------------
1 1 2 Primary
2 2 1 Secondary
2 2 3 Primary
我想知道每个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
此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 };
但是要使用导航,我得到的最接近的是:我分别在每个类上添加了一个属性,并使用流利的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();
这可以检索数据库中已经存在的记录。
但是要将新记录插入到Segment中,其中Category中的originId和destinationId不存在,它将抱怨originId和destinationId不满足外键约束。没关系,因为它们在另一个表中不存在。
它们实际上不是外键,只是用于连接它们的一列,但我不知道如何使用导航属性+ Fluent Api进行设置。
答案 0 :(得分:1)
第一:
[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;
}
}
使用方法:
services.AddDbContext<MyDbContext>(options =>
{
options.UseSqlServer(Default);
options.ReplaceService<IMigrationsModelDiffer, MigrationsModelDifferWithoutForeignKey>();
});
答案 1 :(得分:0)
抱歉 - 这个派对有点晚了。
我刚刚进行了类似的练习。你可以试试这个:
modelBuilder.Entity<Category>()
.HasMany(category => category.Segments)
.WithOne(segment => segment.Category)
.HasPrincipalKey(category => new { category.OriginId, category.DestinationId })
.IsRequired(false)
.OnDelete(DeleteBehavior.NoAction);