0

这是我的问题的简化版本。考虑一个调查应用程序:

  • 有问题,
  • 一个问题有很多选项,一个选项只能属于一个问题
  • 一个问题应该有一个类别,一个类别可以属于多个问题
  • 一个问题可能有很多标签,一个标签可以属于很多问题

此外,我想定义一个问题和其他问题的选项之间的依赖关系,例如,如果用户对“你有车吗?”投了“否”选项。问题,他/她不会被问到“你的车是什么牌子的?”。

由 Code First 创建的自动生成的数据库模式和由数据库首先创建的自动生成的代码都不令人满意。下面是为这两种方法生成的代码和数据库模式。

EF 可以按预期处理 Question - Tag 关系,但它无法处理 Question 和 Option 之间的依赖关系(据我所知),因为 Question 已经有自己的选项。

就 EF 而言,这种情况下理想的代码/数据库设计是什么?

代码优先 - 代码

public class Question
{
    public long Id { get; set; }
    public string Text { get; set; }
    public virtual ICollection<Tag> Tags { get; set; }
    public virtual ICollection<Option> Options { get; set; }
    public virtual ICollection<Option> DependencyOptions { get; set; }

    [ForeignKey("CategoryId")]
    public virtual Category Category { get; set; }
    public long CategoryId { get; set; }
}

public class Option
{
    public long Id { get; set; }
    public string Text { get; set; }

    public virtual ICollection<Question> DependencyQuestions { get; set; }

    [ForeignKey("QuestionId")]
    public virtual Question Question { get; set; }
    public long QuestionId { get; set; }
}

public class Tag
{
    public long Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Question> Questions { get; set; }
}
public class Category
{
    public long Id { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Question> Questions { get; set; }
}

代码优先 - 表格

代码优先 - 表格

Db First - 表

Db First - 表

数据库优先 - 代码

public class Question
{    
    public long Id { get; set; }
    public string Text { get; set; }
    public long CategoryId { get; set; }

    public virtual Category Category { get; set; }
    public virtual ICollection<Option> Options { get; set; }
    public virtual ICollection<Option> Options1 { get; set; }
    public virtual ICollection<Tag> Tags { get; set; }
}

public class Option
{    
    public long Id { get; set; }
    public string Text { get; set; }
    public long QuestionId { get; set; }

    public virtual Question Question { get; set; }
    public virtual ICollection<Question> Questions { get; set; }
}

public class Tag
{    
    public long Id { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Question> Questions { get; set; }
}

public class Category
{    
    public long Id { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Question> Questions { get; set; }
}
4

2 回答 2

0

我不确定我是否理解正确,但我认为您将需要(并且已经拥有)一个many-to-manyon Option/Question- 另外,您在它们之间还有一个一对多(所有者)关系。

我没有足够的知识来讨论这是否是最好的解决方案——但是相信你的“依赖”关系(和另一个)......

...为此,您需要“微调”您的关系和索引表。EF/CF 在后台创建默认的,我认为你需要的是在流畅的配置中创建关系 - 并自己添加额外的列。

而且我通常建议在代码中进行自己的配置 - 与属性/默认配置 - 就像在任何复杂的场景中一样,它可以为您提供更多选项和控制,更少的错误等。就我而言,我只想知道哪些表和列是为我创建的。

这是一个特定的示例代码(我删除了其他不必要的表,只是 Q/O)...

public class QuestionContext : DbContext
{
    public DbSet<Question> Questions { get; set; }
    public DbSet<Option> Options { get; set; }
    public DbSet<QuestionOption> QuestionOptions { get; set; }
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<QuestionOption>()
            .HasKey(i => new { i.OptionID, i.QuestionID });

        modelBuilder.Entity<QuestionOption>()
            .HasRequired(i => i.Opiton)
            .WithMany(u => u.DependencyOptions)
            .HasForeignKey(i => i.OptionID)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<QuestionOption>()
            .HasRequired(i => i.Question)
            .WithMany(u => u.DependencyOptions)
            .HasForeignKey(i => i.QuestionID)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<Option>()
            .HasRequired(i => i.Question)
            .WithMany(u => u.Options)
            .HasForeignKey(i => i.QuestionId)
            .WillCascadeOnDelete(false);
    }
}
public class Question
{
    public long Id { get; set; }
    public string Text { get; set; }
    public virtual ICollection<Option> Options { get; set; }
    public virtual ICollection<QuestionOption> DependencyOptions { get; set; }
}
public class Option
{
    public long Id { get; set; }
    public string Text { get; set; }
    // [ForeignKey("QuestionId")]
    public virtual Question Question { get; set; }
    public long QuestionId { get; set; }
    public virtual ICollection<QuestionOption> DependencyOptions { get; set; }
}
public class QuestionOption
{
    public long OptionID { get; set; }
    public Option Opiton { get; set; }
    public long QuestionID { get; set; }
    public Question Question { get; set; }
    public int DependencyType { get; set; }
    public string DependencyNote { get; set; }
    public bool Active { get; set; }
    public bool UseEtc { get; set; }
}

通过以下迁移...

        CreateTable(
            "dbo.Questions",
            c => new
                {
                    Id = c.Long(nullable: false, identity: true),
                    Text = c.String(maxLength: 4000),
                })
            .PrimaryKey(t => t.Id);

        CreateTable(
            "dbo.Options",
            c => new
                {
                    Id = c.Long(nullable: false, identity: true),
                    Text = c.String(maxLength: 4000),
                    QuestionId = c.Long(nullable: false),
                })
            .PrimaryKey(t => t.Id)
            .ForeignKey("dbo.Questions", t => t.QuestionId)
            .Index(t => t.QuestionId);

        CreateTable(
            "dbo.QuestionOptions",
            c => new
                {
                    OptionID = c.Long(nullable: false),
                    QuestionID = c.Long(nullable: false),
                    DependencyType = c.Int(nullable: false),
                    DependencyNote = c.String(maxLength: 4000),
                    Active = c.Boolean(nullable: false),
                    UseEtc = c.Boolean(nullable: false),
                })
            .PrimaryKey(t => new { t.OptionID, t.QuestionID })
            .ForeignKey("dbo.Options", t => t.OptionID)
            .ForeignKey("dbo.Questions", t => t.QuestionID)
            .Index(t => t.OptionID)
            .Index(t => t.QuestionID);

您定义了两个单独的关系(一个一对多和另一个 m-to-m),并排。

QuestionOption表中,您现在可以手动指定您需要为依赖项添加的所有内容(这就是您的 DependencyOption - 我只是认为这个命名可以更清楚地说明它)。所以你会有类似的东西Question(A) -> allows Option(B)- 但考虑到你的逻辑,你可能需要添加更多。

看起来您需要在两者之间建立关系Question, Question, Option- 所以 3 个索引等。鉴于上面的代码,如果需要,您可以通过一个简单的扩展来做到这一点。

modelBuilder.Entity<QuestionOption>()
    .HasKey(i => new { i.OptionID, i.QuestionLeftID, i.QuestionRightID });
modelBuilder.Entity<QuestionOption>()
    .HasRequired(i => i.Opiton)
    .WithMany(u => u.DependencyOptions)
    .HasForeignKey(i => i.OptionID)
    .WillCascadeOnDelete(false);
modelBuilder.Entity<QuestionOption>()
    .HasRequired(i => i.QuestionLeft)
    .WithMany(u => u.DependencyOptionsLeft)
    .HasForeignKey(i => i.QuestionLeftID)
    .WillCascadeOnDelete(false);
modelBuilder.Entity<QuestionOption>()
    .HasRequired(i => i.QuestionRight)
    .WithMany(u => u.DependencyOptionsRight)
    .HasForeignKey(i => i.QuestionRightID)
    .WillCascadeOnDelete(false);
modelBuilder.Entity<Option>()
    .HasRequired(i => i.Question)
    .WithMany(u => u.Options)
    .HasForeignKey(i => i.QuestionId)
    .WillCascadeOnDelete(false);
public class Question
{
    public long Id { get; set; }
    public string Text { get; set; }
    public virtual ICollection<Option> Options { get; set; }
    public virtual ICollection<QuestionOption> DependencyOptionsLeft { get; set; }
    public virtual ICollection<QuestionOption> DependencyOptionsRight { get; set; }
}
public class QuestionOption
{
    public long QuestionLeftID { get; set; }
    public Question QuestionLeft { get; set; }
    public long QuestionRightID { get; set; }
    public Question QuestionRight { get; set; }
    public long OptionID { get; set; }
    public Option Opiton { get; set; }
    public int DependencyType { get; set; }
    public string DependencyNote { get; set; }
    public bool Active { get; set; }
    public bool AllowForbid { get; set; }
}


对于更复杂的场景(在流畅的代码中使用多对多和手动定义关系 - 我推荐) - 看看我刚才制作的这些详细示例 - 它具有您可能需要的大部分映射。

使用codefirst或fluent API与同一实体的多对多(连接表)关系?

联接表中的 Code First Fluent API 和导航属性

带有附加数据的 EF 代码优先多对多

...如果您需要任何快速指示 - 让我知道问题 - 以及更多细节

于 2013-03-25T22:19:55.550 回答
0

我的理解是您希望后续问题依赖于先前问题的答案...

我的建议是在问题和选项之间建立一个关系表,称为RequiredAnswer。这种关系将表示用户必须已使用相关选项的一个或全部(由您实施)回答了一个或多个问题(您的图表不包括答案表)。然后由代码实现来决定如何使用这种关系。

于 2013-03-25T22:58:00.343 回答