【问题标题】:How to insert data with entiity Framework core when the id is autogenerated?自动生成id时如何使用实体框架核心插入数据?
【发布时间】:2017-08-09 22:28:46
【问题描述】:

我需要在我的数据库的 2 个表中插入一行。这两张表之间的关系是一对多的。这是他们的模型

public partial class Task
{
    public Task()
    {
        TaskOwner = new HashSet<TaskOwner>();
    }

    public int Id { get; set; }
    public string Action { get; set; }
    public int ActualEffort { get; set; }
    public DateTime AssignationDate { get; set; }
    public int EstEffort { get; set; }
    public DateTime EstEnd { get; set; }
    public bool IsComplete { get; set; }
    public int ProjectId { get; set; }
    public string Status { get; set; }

    public virtual ICollection<TaskOwner> TaskOwner { get; set; }
    public virtual Project Project { get; set; }
}

public partial class TaskOwner
{
    public int Id { get; set; }
    public int EmployeId { get; set; }
    public int TaskId { get; set; }

    public virtual Employe Employe { get; set; }
    public virtual Task Task { get; set; }
}

这是上下文

modelBuilder.Entity<Task>(entity =>
        {
            entity.HasIndex(e => e.ProjectId)
                .HasName("FKTask25514");

            entity.Property(e => e.Id)
                .HasColumnName("id")
                .HasColumnType("int(10)");

            entity.Property(e => e.Action)
                .IsRequired()
                .HasColumnName("action")
                .HasColumnType("varchar(255)");

            entity.Property(e => e.ActualEffort)
                .HasColumnName("actual_effort")
                .HasColumnType("int(10)");

            entity.Property(e => e.AssignationDate).HasColumnName("assignation_date");

            entity.Property(e => e.EstEffort)
                .HasColumnName("est_effort")
                .HasColumnType("int(10)");

            entity.Property(e => e.EstEnd).HasColumnName("est_end");

            entity.Property(e => e.IsComplete).HasColumnType("tinyint(1)");

            entity.Property(e => e.ProjectId)
                .HasColumnName("project_id")
                .HasColumnType("int(10)");

            entity.Property(e => e.Status)
                .IsRequired()
                .HasColumnName("status")
                .HasColumnType("varchar(255)");

            entity.HasOne(d => d.Project)
                .WithMany(p => p.Task)
                .HasForeignKey(d => d.ProjectId)
                .OnDelete(DeleteBehavior.Restrict)
                .HasConstraintName("FKTask25514");
        });

        modelBuilder.Entity<TaskOwner>(entity =>
        {
            entity.ToTable("Task_Owner");

            entity.HasIndex(e => e.EmployeId)
                .HasName("FKTask_Owner320287");

            entity.HasIndex(e => e.TaskId)
                .HasName("FKTask_Owner395416");

            entity.Property(e => e.Id)
                .HasColumnName("id")
                .HasColumnType("int(10)");

            entity.Property(e => e.EmployeId)
                .HasColumnName("employe_id")
                .HasColumnType("int(10)");

            entity.Property(e => e.TaskId)
                .HasColumnName("task_id")
                .HasColumnType("int(10)");

            entity.HasOne(d => d.Employe)
                .WithMany(p => p.TaskOwner)
                .HasForeignKey(d => d.EmployeId)
                .OnDelete(DeleteBehavior.Restrict)
                .HasConstraintName("FKTask_Owner320287");

            entity.HasOne(d => d.Task)
                .WithMany(p => p.TaskOwner)
                .HasForeignKey(d => d.TaskId)
                .OnDelete(DeleteBehavior.Restrict)
                .HasConstraintName("FKTask_Owner395416");
        });

一个Task可以被多个TaskOwner拥有。我试过这个方法:

Models.Task task = new Models.Task()
                    {
                        Status = tde.Status.ToString(),
                        Action = tde.Subject.ToString(),
                        IsComplete = tde.IsComplete,
                        EstEnd = tde.DueDate.GetValueOrDefault(DateTime.Now),
                        AssignationDate = tde.DateTimeCreated.Date,
                        ActualEffort = Convert.ToInt32(tde.ActualWork),
                        EstEffort = Convert.ToInt32(tde.TotalWork),
                        ProjectId = (from p in _context.Project
                                     where p.ProjectSapId == Convert.ToInt32(tde.BillingInformation)
                                     select p.Id).First(),
                    };

                    Models.TaskOwner taskOwner = new Models.TaskOwner()
                    {
                        EmployeId = employee.Id,
                    };
                    task.TaskOwner.Add(taskOwner);
                    _context.Task.Add(task);

然后将其保存到数据库中,但我总是查看此错误:

检测到类型为 'Models.TaskOwner' 的自引用循环。路径'[6].taskOwner[0].employe.taskOwner'。

如果可以的话,我会使用我刚刚创建的任务的 id 并将其链接到 taskOwner 模型,但由于 id 是自动生成的,我不能或者至少我不知道如何生成。我究竟做错了什么?做我想要完成的事情的正确方法是什么?

【问题讨论】:

    标签: mysql entity-framework linq entity-framework-core asp.net-core-webapi


    【解决方案1】:

    所以Task 有零个或多个TaskOwners。每个TaskOwner 恰好有一个Task。没有Task,就没有TaskOwners

    这意味着,如果你在数据库中有一个现有的TaskOwner,你就不能让它拥有第二个Task。但是,您可以让它拥有一个不同的Task,而不是它现在拥有的。

    如果您向数据库中引入(添加)一个与数据库中现有对象有关系的对象,您可以使用相关对象的 Id 或对象本身(当然它有一个非零身份证)。

    如果相关对象也是新的,只需将相关对象分配给您将添加的对象。您不必单独添加相关对象。

    请注意,只要您没有使用 SaveChanges,您就不能使用任何新对象的 Id。

    引入拥有现有任务和现有员工的 TaskOwner

    using (var dbContext = new MyDbContext())
    {
        int employeeId = FetchEmployeeId(...);
        int taskId = FetchTaskId(...);
    
        TaskOwner introducedTaskOwner = dbContext.TaskOwners.Add(new TaskOwner()
        {
            // don't fill the ID!
            EmployeeId = employeeId,
            TaskId = taskId,
    
            ... // other properties
        });
        dbContext.SaveChanges();
    
        // now the introduced task owner has an Id!
        return introducedTaskOwner;
    }
    

    引入一个 TaskOwner 和一个新的 Task。使用现有员工

    现在你不能填写TaskId,而是填写一个Task:

    using (var dbContext = new MyDbContext())
    {
        int employeeId = FetchEmployeeId(...);
    
        TaskOwner introducedTaskOwner = dbContext.TaskOwners.Add(new TaskOwner()
        {
            // don't fill the TaskOwner ID!
            EmployeeId = employeeId,
    
            // Give the Task owner a non-existing Task:
            Task = new Task()
            {
                // don't fill the Task ID!
                // don't fill the collection of TaskOwners
    
                ... // fill other Task properties
            }
    
            ... // other task owner properties
        });
        dbContext.SaveChanges();
    
        // now both the task owner and the task are introduced. Both have an ID
        Debug.Assert(introducedTaskOwner.Id != 0);
        Debug.Assert(intrducedTaskOwner.TaskId != 0);
        Debug.Assert(introducedTaskOwner.Task.Id != 0);
    }
    

    如果您愿意,您可以先介绍 Task,然后再介绍 TaskOwner。只要你没有使用 SaveChanges,你就不能使用任何 Id。

    using (var dbContext = new MyDbContext())
    {
        int employeeId = FetchEmployeeId(...);
    
        Task introducedTask = dbContext.Tasks.Add(new Task()
        {
            // don't fill the Task ID!
            TaskOwners = new List<TaskOwner>(); // This Task has no owners yet
    
                ... // fill other Task properties
        });
    
        TaskOwner introducedTaskOwner = dbContext.TaskOwners.Add(new TaskOwner()
        {
            // don't fill the TaskOwner ID!
            EmployeeId = employeeId,
    
            // Give the Task owner a the introduced task
            Task = introducedTask,
    
            ... // other task owner properties
        });
        dbContext.SaveChanges();
    }
    

    您也可以反过来做:您可以将 TaskOwner 添加到现有(或新添加的)任务中

    using (var dbContext = new MyDbContext())
    {
        int employeeId = FetchEmployeeId(...);
    
        // get existing task:
        Task existingTask = dbContext.Tasks
            .Where(task => task.ActualEffort > ...)
            .OrderBy(task => task.ActualEffort)
            .FirstOrDefault();
    
        // add an owner to the task owners collection:
        existingTask.TaskOwners.Add(new TaskOwner()
        {
            // don't fill the TaskOwner ID!
            EmployeeId = employeeId,
            // no need to fill the TaskId, nor the Task
            // Entity Framework knows the Task to which it belongs, because you 
            // add it to the TaskOwners collection
            ...           
        });
        dbContext.SaveChanges();
    }
    

    结论:只要没有Id,就使用完整的对象。如果对象还没有添加,实体框架会添加它

    【讨论】:

    • 非常感谢!通过您的回答,我意识到我在 BD 的结构上犯了一个错误。它现在运行良好!
    【解决方案2】:

    首先您需要保存Models.Task 并访问自动生成的ID,然后保存TaskOwner 模型。

    【讨论】:

    • 有没有更好的方法,我不需要在一次调用中添加的每项任务在数据库中保存两次?我读过 save 是一个非常昂贵的调用。
    • @MarcelJr.SamsonMorasse 出现了一些问题,因此无法给您正确的答案。很好,您找到了解决方案。干杯。
    猜你喜欢
    • 2015-10-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多