Платформа сущностей пытается вставить существующую родительскую запись при попытке добавить дочернюю запись - PullRequest
0 голосов
/ 04 октября 2018

Я новичок в использовании EF (раньше использовал Dapper, в основном), и у меня возникли проблемы в приложении API, которое я взял на себя в большинстве случаев.

У меня есть метод (созданный предыдущим разработчиком), который создает илиобновление userTerminal.Он имел FK для Terminal текущей записи, которую я добавляю к userTerminal, привязанной к терминалу, который уже существует.

Это метод

    public void AddTerminalUser(UserTerminal userTerminal)
    {
        using (var context = new GateManagementEntities(connectionString))
        {
            //Modified existing
            if (context.UserTerminals.Count(z => z.TerminalCode == userTerminal.TerminalCode && z.UserId == userTerminal.UserId) > 0)
            {
                UserTerminal ut = new UserTerminal() { Id = userTerminal.Id, IsDeleted = true, LastUpdated = DateTime.Now };
                var entry = context.Entry(ut);
                entry.Property(z => z.LastUpdated).IsModified = true;
                entry.Property(z => z.IsDeleted).IsModified = true;
            }
            else
            {
                //New Entity 
                userTerminal.CreatedDate = DateTime.Now;
                userTerminal.LastUpdated = DateTime.Now;
                context.UserTerminals.Add(userTerminal);
            }
            context.SaveChanges();
        }
    }

, и этот класс передается в

    public partial class UserTerminal
    {
        public int Id { get; set; }
        public string UserId { get; set; }
        public Nullable<int> TerminalCode { get; set; }
        public Nullable<bool> IsDeleted { get; set; }
        public Nullable<System.DateTime> LastUpdated { get; set; }
        public Nullable<System.DateTime> CreatedDate { get; set; }
        public string UpdatedBy { get; set; }

        public virtual Terminal Terminal { get; set; }
        public virtual UserPreference UserPreference { get; set; }
    }

    public partial class Terminal
    {
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
        public Terminal()
        {
            this.GateAssignments = new HashSet<GateAssignment>();
            this.GateAssignments1 = new HashSet<GateAssignment>();
            this.GateDefinitions = new HashSet<GateDefinition>();
            this.UserTerminals = new HashSet<UserTerminal>();
        }

        public int TerminalCode { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public Nullable<System.DateTime> LastUpdated { get; set; }
        public Nullable<System.DateTime> CreatedDate { get; set; }
        public string UpdatedBy { get; set; }
        public Nullable<int> GateTypeId { get; set; }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
        public virtual ICollection<GateAssignment> GateAssignments { get; set; }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
        public virtual ICollection<GateAssignment> GateAssignments1 { get; set; }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
        public virtual ICollection<GateDefinition> GateDefinitions { get; set; }
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
        public virtual ICollection<UserTerminal> UserTerminals { get; set; }
        public virtual GateType GateType { get; set; }
    }

TerminalCode, что связывает Terminal с TerminalUser Iпроверил переданный объект UserTerminal и TerminalCode UserTerminal, и он заполняется ожидаемым кодом терминала (я знаю, что код терминала находится в таблице терминалов), а объект Terminal также полностью заполняется с помощью TerminalCode Я ожидаю.Он пытается вставить уже существующую запись терминала в базу данных, что вызывает ошибку дублирования PK.

Как получить ее только на весь терминал, если она еще не существует?Кажется, он следует той же схеме, что и другие похожие методы, поэтому я не смог понять, почему это не так.

1 Ответ

0 голосов
/ 05 октября 2018

Передача сущностей между экземплярами контекста хлопотна.Проблема, когда ваш контекст не знает о UserTerminal, пока этот новый UserTerminal ссылается на реальную существующую запись терминала, заключается в том, что новый экземпляр Context также не знает об этом терминале.

Если вы загружаете терминал (илюбые другие связанные объекты) в контекст перед добавлением UserTerminal, он должен работать, однако для безопасности обычно лучше связать новый UserTerminal со ссылками на терминал, который известен контексту.

Например:

   public void AddTerminalUser(UserTerminal userTerminal)
    {
        using (var context = new GateManagementEntities(connectionString))
        {
            //Modified existing
            if (context.UserTerminals.Count(z => z.TerminalCode == userTerminal.TerminalCode && z.UserId == userTerminal.UserId) > 0)
            {
                UserTerminal ut = new UserTerminal() { Id = userTerminal.Id, IsDeleted = true, LastUpdated = DateTime.Now };
                var entry = context.Entry(ut);
                entry.Property(z => z.LastUpdated).IsModified = true;
                entry.Property(z => z.IsDeleted).IsModified = true;
            }
            else
            {
                //New Entity 
                userTerminal.CreatedDate = DateTime.Now;
                userTerminal.LastUpdated = DateTime.Now;
                var terminal = context.Terminals.Find(userTerminal.TerminalCode); 
                userTerminal.Terminal = terminal;
                // Repeat above for all other references.
                context.UserTerminals.Add(userTerminal);
            }
            context.SaveChanges();
        }
    }

Я не советую передавать объекты за пределы области действия DbContext, который их загружал.ViewModels / DTO должны служить этой цели и избегать неожиданностей, когда вы ожидаете, что ссылки FK разрешатся автоматически.В некоторых случаях контексты разрешают ссылки, казалось бы, иногда, потому что связанный объект уже загружен, но затем происходит сбой с дублирующимися PK в сценариях, где объект ранее не загружался в контекст.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...