EF Core-在父子关系上使用SQL身份的外键约束

时间:2018-12-17 20:29:17

标签: sql entity-framework .net-core entity-framework-core

我有一个已经存在的数据库,我们正在尝试使用EF Core。因此,我的情况是两个表的修改后的*父子关系自动增加。这是我为他们准备的EF Core代码:

            modelBuilder.Entity<RunActualRow>(model =>
        {
            model.ToTable("RunActual");
            model.Property(a => a.ID).UseSqlServerIdentityColumn().HasColumnName("ID");
            model.HasKey(a => a.ID);
            model.HasOne(d => d.RunRow)
                .WithMany(p => p.RunActualRows)
                .HasForeignKey(d => new { d.RunID})
                .HasConstraintName("FK_RunActual_Run");
        });

        modelBuilder.Entity<RunRow>(entity =>
        {
            entity.ToTable("Run");
            entity.Property(e => e.RunID).UseSqlServerIdentityColumn();
            entity.HasKey(e => new { e.RunID });
        });

因此,基本上在上面,您看到的是RunRow是父实体,而RunActualRow是子实体。 RunIDRunRow)是SQL身份生成的RunActualRow的外键。

当我尝试在一个RunRow对象中声明父项和子项的插入操作时,收到错误消息,提示实体框架的外键丢失。在sql server为Runs行分配PK之前,我无法设置外键。但是,EF Core似乎没有正确设置该外键,相反EF Core只是给了我一个错误,而Profiler显示我在尝试输入无效FK为0的RunActual行。

有人遇到过这种特定情况吗?如果是,这是一个已知问题吗?或者我错过了什么?我需要做这两个单独的交易吗?

  • 为什么要修改?因为在这种情况下,我们实际上并没有严格要求您必须要有父母才能生孩子。

2 个答案:

答案 0 :(得分:0)

好的。 “发布简短但完整的副本”,我的意思是这样的(但实际上失败了):

using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel.DataAnnotations;
//using Microsoft.Samples.EFLogging;
using System.Data.SqlClient;
using System.Xml.Linq;
using System.Threading.Tasks;

namespace EFCore2Test
{

   public class RunRow
    {
        public int RunID { get; set; }
        public ICollection<RunActualRow> RunActualRows { get; } = new HashSet<RunActualRow>();
    }
    public class RunActualRow
    {
        public int ID { get; set; }

        public int RunID { get; set; }
        public RunRow RunRow { get; set; }
    }

    public class Db : DbContext
    {
        public DbSet<RunRow> RunRows { get; set; }

        public DbSet<RunActualRow> RunActualRows { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        { 
            base.OnModelCreating(modelBuilder);
            modelBuilder.Entity<RunActualRow>(model =>
            {
                model.ToTable("RunActual");
                model.Property(a => a.ID).UseSqlServerIdentityColumn().HasColumnName("ID");
                model.HasKey(a => a.ID);
                model.HasOne(d => d.RunRow)
                    .WithMany(p => p.RunActualRows)
                    .HasForeignKey(d => new { d.RunID })
                    .HasConstraintName("FK_RunActual_Run");
            });

            modelBuilder.Entity<RunRow>(entity =>
            {
                entity.ToTable("Run");
                entity.Property(e => e.RunID).UseSqlServerIdentityColumn();
                entity.HasKey(e => new { e.RunID });
            });
        }
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer("Server=(local);Database=EFCoreTest;Trusted_Connection=True;MultipleActiveResultSets=true");
            base.OnConfiguring(optionsBuilder);
        }
    }



    class Program
    {


        static void Main(string[] args)
        {
            using (var db = new Db())
            {
                db.Database.EnsureDeleted();

                db.Database.EnsureCreated();
                db.Database.ExecuteSqlCommand("alter table RunActual drop constraint FK_RunActual_Run");

            }
            using (var db = new Db())
            { 

                //db.ConfigureLogging(s => Console.WriteLine(s));

                var r = new RunRow();
                db.RunRows.Add(r);

                db.SaveChanges();

                r.RunActualRows.Add(new RunActualRow());
                r.RunActualRows.Add(new RunActualRow());
                r.RunActualRows.Add(new RunActualRow());

                db.SaveChanges();
            }

            using (var db = new Db())
            {
                var count = db.RunActualRows.Count();
                Console.WriteLine($"{count} RunActualRows");
            }


            Console.WriteLine("Hit any key to exit");
            Console.ReadKey();
        }
    }
}

答案 1 :(得分:0)

不是确实是一个Entity Framework Core错误,尽管我肯定会争辩说Entity Framework可以更好地处理结果集以弄清楚实际发生了什么,或者引发更多信息错误您知道它正在查看并返回的结果集不包含您所请求的查询。

感谢David帮助我解决了这一问题-我不确定我是否会想到创建一个新的表结构来模仿旧的表结构,就像看到它确实起作用一样。

该错误原来是由于数据库上的一个触发器引起的-该触发器正在运行一个存储过程,该存储过程转过来并返回1或0结果集,以说明是否存在错误(如果没有错误,则为0;如果有错误,则为1)。

由于此,这将导致实体框架选择该值(即使结果集列名称与它在---中发送的select语句不匹配,这在IMO中可以更好地处理)和子表尝试将其用作外键。由于0不是有效的FK,因此会出错。

有趣的是,我认为如果我得到了1(错误),它会试图将其用作外键,这会起作用,因为存在 IS 记录为1作为主键。

总结一下-如果有人遇到此问题,请运行您的探查器,查看Entity Framework正在发送什么,以及查询的结果是什么(如果看到返回了多个结果集),请执行看一下正在运行的触发器。

谢谢!