EF Core - ограничения внешнего ключа с использованием идентификатора SQL в отношении «родитель / потомок» - PullRequest
0 голосов
/ 17 декабря 2018

У меня уже есть база данных, с которой мы пытаемся использовать EF Core.Из-за этого у меня возникает ситуация, когда две таблицы с измененными отношениями * Parent / Child автоматически увеличиваются.Вот код 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 является дочерней сущностью.RunID (RunRow) - это внешний ключ, сгенерированный идентификатором SQL, для RunActualRow.

Когда я пытаюсь выполнить вставку, в которой я объявляю родителей и детей в одном объекте RunRow, я получаюошибка, что внешние ключи Entity Framework отсутствуют.Я не могу установить внешний ключ до тех пор, пока сервер sql не назначит Runs строку PK ... однако, EF Core, похоже, не устанавливает это правильно, вместо этого EF Core просто выдает мне ошибку, и Profiler показывает, что я пытаюсьвведите строку RunActual с недопустимым FK, равным 0.

Кто-нибудь сталкивался с этим конкретным сценарием, и если да, то это известная проблема или я что-то упустил?Нужно ли совершать эти две отдельные транзакции?

  • ПОЧЕМУ МОДИФИЦИРОВАНО?потому что в этом сценарии мы на самом деле не строго соблюдаем необходимость наличия родителя, чтобы иметь ребенка.

Ответы [ 2 ]

0 голосов
/ 18 декабря 2018

Случилось так, что НЕ действительно является ошибкой Entity Framework Core, хотя я бы определенно утверждал, что Entity Framework мог бы лучше обрабатывать наборы результатов, чтобы выяснить, что на самом деле происходит, или выдать более информативную ошибку, позволяющую вамзнаю, что в наборе результатов, который он просматривал и возвращал, не было запрошенного вами запроса.

Спасибо Дэвиду за помощь в выяснении этого - я не уверен, что подумал бы о создании новогоструктура таблицы, которая имитировала старую таблицу, чтобы увидеть, что она действительно работает.

Ошибка возникла из-за триггера в базе данных - этот триггер выполнял хранимую процедуру, которая развернулась и вернула набор результатов 1 или 0, чтобы сказать, был лиошибка (0, если ошибки не было, 1, если произошла ошибка).

Из-за этого структура сущностей может подобрать это значение (даже если имя столбца набора результатов не соответствует выборузаявление, которое он послал в --- что-то, что могло бы быть обработано лучше IMO), и на дочерней таблице попытайтесь использовать это как внешний ключ.Поскольку 0 не был действительным FK, это выдает ошибку.

Достаточно забавно, я думаю, что если бы я получил 1 (Ошибка), он попытался бы использовать его в качестве внешнего ключа, что сработало бы, потому что IS записьс первичным ключом 1.

В заключение: если кто-то сталкивается с этой проблемой, запустите ваш профилировщик и посмотрите, что Entity Framework отправляет, и каков результат запроса, если вы видите, чтоВы получаете несколько наборов результатов, посмотрите на запускаемые триггеры.

Спасибо всем!

0 голосов
/ 18 декабря 2018

Хорошо.Под «размещать короткое, но полное репродукцию» я имел в виду нечто подобное (но на самом деле это не сработало):

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();
        }
    }
}
...