【问题标题】:One to many relationship mapping error with code first 6.0代码优先 6.0 的一对多关系映射错误
【发布时间】:2014-04-17 12:46:51
【问题描述】:

我有两个实体Task和Attempt,一个任务有很多Attempt,所以我定义的实体如下。

public class Task
{
    public long TaskId { get; set; }
    [Required]
    public int DestinationNumber { get; set; }
    [Required]
    public int CountryCode { get; set; }
    // blah blah
    public virtual ICollection<Attempt> Attempts { get; set; }
}

public class Attempt
{
    public long Id { get; set; }
    public string AttemptsMetaData { get; set; }
    public DateTime Time { get; set; }
    public bool Answered { get; set; }
    public DateTime Disconnected { get; set; }
    // foreign key
    public long TaskId { get; set; }
    public virtual Task Task { get; set; }
}

我使用 Code First 关系 Fluent API 来映射关系。

public class OutboundContext : DbContext
{
    public DbSet<Task> Tasks { get; set; }
    public DbSet<Attempt> Attempts { get; set; }
    public OutboundContext()
        : base("Outbound")
    {
        Database.SetInitializer<OutboundContext>(new CreateDatabaseIfNotExists<OutboundContext>());
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Task>().HasMany(t => t.Attempts).WithRequired();
    }

单元测试已通过,但是当我检查表 [Outbound].[dbo].[Attempts] 时。列是

   [Id]
  ,[AttemptsMetaData]
  ,[Time]
  ,[Answered]
  ,[Disconnected]
  ,[Task_TaskId]
  ,[Task_TaskId1]

您看到第一个 [Task_TaskId] 是错误的,并且生成了一个额外的列 [Task_TaskId1]

怎么了?

编辑

TaskId 应该是外键。

// Add dummy data
        public bool AddAttempt(List<Attempt> attempt)
        {
            using (OutboundContext db = new OutboundContext())
            {
                foreach (var item in attempt)
                {
                    db.Attempts.Add(item);
                }
                try
                {
                    db.SaveChanges();
                    return true;
                }
                catch(Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    return false;
                }
            }
        }

        List<Attempt> attempts = new List<Attempt>();
        Attempt objAtt = new Attempt();
        long id = 123456789;
        objAtt.AttemptId = id;
        objAtt.Time = DateTime.Now;
        objAtt.AttemptsMetaData = "test";
        objAtt.Answered = true;
        objAtt.Disconnected = DateTime.Now;
        objAtt.TaskId = 33333333;
        attempts.Add(objAtt);
        //Then call AddAttempt

【问题讨论】:

  • 您是否尝试过自己在代码中添加TaskID(在尝试类中)?这应该在您进行迁移时整理出重复的 task_ids。
  • 是的,如果您查看 msdn.microsoft.com/en-us/data/jj591583.aspx。它说:代码优先不能自己匹配两个类中的属性。 Posts 的数据库表应该有一个用于 CreatedBy 人和一个用于 UpdatedBy 人的外键,但代码首先会创建四个外键属性:Person_Id、Person_Id1、CreatedBy_Id 和 UpdatedBy_Id。
  • 我们在这里讨论的是基本的一对多关系,我建议在 OnModelCreating 中删除您的流畅 API 代码并进行新的迁移
  • 我可以将 Fluent API 和 DataAnnotations 混合在一起吗?
  • 是的,但我认为在这种情况下,流畅的 API 会干扰代码优先约定。它应该首先使用代码。

标签: c# .net entity-framework ef-code-first


【解决方案1】:

似乎问题的发生是因为您在 Task 类中拥有的 Attempt 集合实际上并未引用到您在 Attempt 类中拥有的“Task”属性。我的建议是在 Attempt 类中明确声明一个 int TaskId 属性,并按以下方式为 Attempt 类进行映射:

HasRequired(attempt => attempt.Task)
            .WithMany(task => task.Attempts)
            .HasForeignKey(attempt => attempt.TaskId);

希望这会有所帮助。

【讨论】:

  • 不正确。智能感知不显示尝试=>尝试。任务
  • 对不起,我提供的语法是 EntityTypeConfiguration。如果您直接处理 OnModelCreating(DbModelBuilder modelBuilder) 它应该类似于: modelBuilder.Entity().HasRequired(attempt => attempt.Task) .WithMany(task => task.Attempts).HasForeignKey(attempt = > 尝试.TaskId);
  • 确实没有 Task_Id1 并且创建了表。但问题是当我添加虚拟数据并执行 db.Savechanges() 时,更新条目时发生错误。有关详细信息,请参阅内部异常。 ---> System.Data.SqlClient.SqlException:列名“TaskId”无效。
  • @Love 我检查了你的代码。这里的问题是您实际上需要保存任务对象而不是尝试。由于尝试实际上取决于任务,因此您可以执行以下操作:在 Task.Attempts 集合中添加尝试,然后将任务对象添加到任务的 context.DbSet 中,然后尝试保存任务对象。我相信这会解决你的问题。
  • -@Sayan。是的,数据被插入到两个表中。但在任务表中,没有“尝试”列。我还将鼠标悬停在 SQL Server 管理工作室中的 TaskId 列上。它显示“无效列”。
【解决方案2】:

使用 Fluent API。

modelBuilder.Entity<Task>().HasMany(t => t.Attempts).WithRequired(t=>t.Task).HasForeignKey(t => t.TaskId);

另一点是我们必须以正确的顺序保存表格。比如:

            db.Tasks.Add(task);
            db.SaveChanges();
            Attempt a = new Attempt() { Id = Guid.NewGuid(), TaskId = task.TaskId, AttemptsMetaData = "1" };
            db.Attempts.Add(a);
            db.SaveChanges();

否则会在 SQL Server Management Studio 中的 TaskId 上显示“无效列”。

【讨论】:

    【解决方案3】:

    我对 OnModelCreating 做了一个非常小的更改,它没有在数据库中创建一个额外的 [Task_TaskId1]:

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Task>().HasMany(t => t.Attempts);
    }
    

    【讨论】:

    • 请添加一些说明,说明这是如何解决问题的,如果可能的话,请提供一些相关文档的链接。
    猜你喜欢
    • 1970-01-01
    • 2019-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多