我有这样的模特
public class Question
{
public string Id { get; set; } = Guid.NewGuid().ToString();
public Answer Answer { get; set; }
public List<Variant> Variants { get; set; }
public string CorrectVariantId { get; set; }
public Variant CorrectVariant { get; set; }
}
public class Variant
{
public string Id { get; set; } = Guid.NewGuid().ToString();
public string QuestionId { get; set; }
public Question Question { get; set; }
}
// mapping
modelBuilder.Entity<Question>()
.HasOne(q => q.CorrectVariant)
.WithOne(v => v.Question)
.HasForeignKey<Question>(q => q.CorrectVariantId);
modelBuilder.Entity<Variant>()
.HasOne(v => v.Question)
.WithMany(a => a.Variants)
.OnDelete(DeleteBehavior.Cascade);
在我从EF RC1
升级到RTM
之前,哪个工作完美无缺。但现在它抛出:
System.InvalidOperationException: Cannot create a relationship between 'Question.Variants' and 'Variant.Question', because there already is a relationship between 'Question.CorrectVariant' and 'Variant.Question'. Navigation properties can only participate in a single relationship.
如果不从Variants
模型中删除Question
属性,是否有解决此问题的方法?
答案 0 :(得分:7)
如果有人遇到这个问题。 这是更优雅的解决方案
public class Question
{
public Guid Id { get; private set; }
public IReadOnlyList<Variant> Variants { get; private set; }
public Guid CorrectVariantId { get; private set; }
public Guid? AnsweredVariantId { get; private set; }
public bool IsAnswerCorrect => CorrectVariantId == AnsweredVariantId;
public bool IsAnswered => AnsweredVariantId != null;
}
public class Variant
{
public Guid Id { get; private set; }
public Guid QuestionId { get; private set; }
public string HiddenUserLogin { get; private set; }
public User HiddenUser { get; private set; }
}
// mapping
mb.Entity<Question>()
.HasMany(q => q.Variants)
.WithOne()
.HasForeignKey(nameof(Variant.QuestionId))
.IsRequired()
.OnDelete(DeleteBehavior.Cascade);
mb.Entity<Question>()
.HasOne(typeof(Variant))
.WithOne()
.HasForeignKey<Question>(nameof(Question.AnsweredVariantId))
.IsRequired(false)
.OnDelete(DeleteBehavior.Restrict);
// EF creates Unique Index for nullable fields
mb.Entity<Question>()
.HasIndex(q => q.AnsweredVariantId)
.IsUnique(false);
// create index instead of FK hence the cyclic dependency between Question and Variant
mb.Entity<Question>()
.HasIndex(q => q.CorrectVariantId)
.IsUnique();
答案 1 :(得分:4)
这是RC1中的一个错误/不受欢迎的行为,它已被修复。
你应该创建另一个属性,比如说其他关系的SecondQuestion。
public class Question
{
public string Id { get; set; } = Guid.NewGuid().ToString();
public List<Variant> Variants { get; set; }
public string CorrectVariantId { get; set; }
public Variant CorrectVariant { get; set; }
}
public class Variant
{
public string Id { get; set; } = Guid.NewGuid().ToString();
public string QuestionId { get; set; }
public Question Question { get; set; }
public Question SecondQuestion { get; set; }
}
你的DbContext:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Question>()
.HasOne(q => q.CorrectVariant)
.WithOne(v => v.SecondQuestion)
.HasForeignKey<Question>(q => q.CorrectVariantId);
modelBuilder.Entity<Variant>()
.HasOne(v => v.Question)
.WithMany(a => a.Variants).HasForeignKey(x => x.QuestionId).OnDelete(DeleteBehavior.SetNull);
base.OnModelCreating(modelBuilder);
}
如何使用它:
using (var myDb = new MyDbContext())
{
var variantFirst = new Variant();
var variantSecond = new Variant();
var question = new Question();
variantFirst.Question = question;
variantSecond.SecondQuestion = question;
myDb.Variants.Add(variantFirst);
myDb.Variants.Add(variantSecond);
myDb.SaveChanges();
}
答案 2 :(得分:1)
给出的两个例子已经让我了解了一部分,但是我想要一个集合和一个具有相同对象类型的项,因此需要在模型上使用与原始问题相同的表。我试图在下面提供一个适用于.NET Core 2.2的简单示例:
public class ParentModel
{
public int Id { get; set; }
// Id for single instance navigation property
public int? ChildModelId { get; set; }
// Single instance navigation property to ChildTable, identified by ChildModelId property as foreign key
public virtual ChildModel ChildModel { get; set; }
// Collection navigation property to ChildTable with identified by ParentId property
public virtual ICollection<ChildModel> ChildModels { get; set; }
}
public class ChildModel
{
public int Id { get; set; }
// Id for ParentModel property back to ParentTable
public int ParentId { get; set; }
// Single instance navigation property to ParentTable, identified by ParentId property as foreign key
public virtual ParentModel ParentModel { get; set; }
}
public class ApplicationDbContext : IdentityDbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<ParentModel>()
.ToTable("ParentTable");
// Configure collection of ChildModels (ParentTable to ChildTable/one-to-many relationship)
builder.Entity<ParentModel>()
.HasMany(t => t.ChildModels)
.WithOne(t => t.ParentModel)
.HasForeignKey(t => t.ParentId)
.IsRequired()
.OnDelete(DeleteBehavior.Cascade);
builder.Entity<ChildModel>()
.ToTable("ChildTable");
// Configure single ChildModel navigation property on ParentModel (one-to-one relationship)
builder.Entity<ParentModel>()
.HasOne(t => t.ChildModel)
.WithOne()
.HasForeignKey(typeof(ParentModel), nameof(ParentModel.ChildModelId))
.IsRequired(false)
.OnDelete(DeleteBehavior.Restrict);
}
}
避免Navigation properties can only participate in a single relationship.
错误的关键是仅将导航属性配置回父表一次。我们使用ChildModels
在ParentTable上的.WithOne(t => t.ParentModel)
集合上对此进行配置。然后,我们就不用再通过将.WithOne()
设置为空来为后续关系配置关系的另一端了,因为如果我们再次进行配置(例如.WithOne(t => t.ParentModel)
),则会出错。
此外,导航属性上的virtual
修饰符还允许lazy loading。