Подтвердить что ты не робот

Родительский объект находится в EntityState.Unchanged, но он все еще вставлен в базу данных

У меня есть простая схема снежинки, из которой я создал мою модель Entity Framework.
Проблема заключается в том, что я пытаюсь сопоставить дочерний объект с существующим родительским и/или дедушканым бабушкой, но он все равно вставляет его.

Я последовал за этим:

Вставить новый объект с существующим объектом
Запретить платформу Entity Framework вставлять значения для навигационных свойств

Интересно, что хотя EntityState родительских объектов "Unchanged", Entity Framework все еще пытается вставить его.


Схема

enter image description here


Метод CarRepository.Save()

public void Save(Car car)
    {
        using (DBContext context = new DBContext())
        {
            // No need to save if it already exists
            if ( context.Cars.FirstOrDefault(x => x.RegistrationNumber == car.RegistrationNumber) != null)
            {
                return;
            }
            else
            {
                // Check if the parent POCOs exist in the DB. 
                Model existingModel = context.Models.FirstOrDefault(x => x.Name == car.Model.Name);
                Manufacturer existingManufacturer = context.Manufacturers.FirstOrDefault(x=> x.Name == car.Model.Manufacturer.Name)
                Trader existingTrader = context.Traders.FirstOrDefault(x=> x.Name == car.Trader.Name)
                TraderCompany existingTraderCompany = context.TraderCompanys.FirstOrDefault(x=> x.Name == car.Trader.TraderCompany.Name)

                context.ContextOptions.LazyLoadingEnabled = false;

                //Attach to the context if existing in the DB, i.e mark the existing POCOs not to be added the DB
                if (existingModel != null)
                {
                    car.Model = existingModel;
                    Assert.IsTrue(context.ObjectStateManager.GetObjectStateEntry(car.Model).State == EntityState.Unchanged);
                }

                if (existingManufacturer != null)
                {
                    car.Model.Manufacturer = existingManufacturer;
                    Assert.IsTrue(context.ObjectStateManager.GetObjectStateEntry(car.Model.Manufacturer).State == EntityState.Unchanged);
                }

                if (existingTrader != null)
                {
                    car.Trader = existingTrader;
                    Assert.IsTrue(context.ObjectStateManager.GetObjectStateEntry(car.Trader).State == EntityState.Unchanged);
                }

                if (existingTraderCompany != null)
                {
                    car.Trader.TraderCompany = existingTraderCompany;
                    Assert.IsTrue(context.ObjectStateManager.GetObjectStateEntry(car.Trader.TraderCompany).State == EntityState.Unchanged);
                }

                //Mark the Car for Addition to the DB
                context.Cars.AddObject(car);
                context.ObjectStateManager.ChangeObjectState(car, EntityState.Added);


                //If the POCOs do not exist in the DB mark them for addition
                if (existingModel == null)
                {
                   context.ObjectStateManager.ChangeObjectState(car.Model,EntityState.Added);
                }

                if (existingManufacturer == null)
                {
                    context.ObjectStateManager.ChangeObjectState(car.Model.Manufacturer,EntityState.Added);
                }

                if (existingTrader == null)
                {
                    context.ObjectStateManager.ChangeObjectState(car.Trader,EntityState.Added);
                }

                if (existingTraderCompany == null)
                {
                    context.ObjectStateManager.ChangeObjectState(car.Trader.TraderCompany,EntityState.Added);
                }

                context.SaveChanges();

            }
        }
    }

Изменить:

После нескольких дней возиться мне удалось придумать обходной путь, который работал у меня.

Кажется, что автомобиль, который передается CarRepository.Save(), имеет какой-то внутренний контекст, который является undetectable... Это так, это невозможно отделить его от этого контекста /s и добавить его в файл CarRepository.Save(). Чтобы действительно добавить его в этот контекст, я глубоко/лениво копирую объект Car и его навигационные свойства, если они есть.


Обходной путь

public void Save(Car car)
{
    using (DBContext context = new DBContext())
    {
        // No need to save if it already exists
        if ( context.Cars
                    .Any(x => x.RegistrationNumber == car.RegistrationNumber))
        {
            return;
        }
        else
        {
            //Assign scalar properties to the deep copy
            Car carToBeSaved = new Car 
            {
                carToBeSaved.RegistrationNumber = car.RegistrationNumber,
                carToBeSaved.Price = car.Price
            }


            //Car -> Trader -> ...
            if(car.Trader != null)
            {   
                Trader existingTrader = 
                    context.Traders
                           .FirstOrDefault(x => x.Name == car.Trader.Name);

                //If exists in DB assign, if not deep copy
                carToBeSaved.Trader = existingTrader ??
                    new Trader
                    {
                        Name = car.Trader.Name,
                        JobTitle = car.Trader.JobTitle
                    }

                //Car -> Trader -> TraderCompany
                if(car.Trader.TraderCompany != null)
                {
                    TraderCompany existingTraderCompany = 
                        context.TradersCompanys
                               .FirstOrDefault(x => x.Name == car.Trader
                                                                 .TraderCompany
                                                                 .Name);

                    //If exists in DB assign, if not deep copy  
                    carToBeSaved.Trader.TraderCompany = existingTraderCompany ??
                        new TraderCompany
                        {
                            Name = car.Trader.TraderCompany.Name,
                            Address = car.Trader.TraderCompany.Address,
                            PhoneNumber = car.Trader.TraderCompany.PhoneNumber
                        }
                }
            }

            //Car -> Model -> ...
            if(car.Model != null)
            {   
                Model existingModel = 
                    context.Models
                           .FirstOrDefault(x => x.Name == car.Model.Name);

                //If exists in DB assign, if not deep copy
                carToBeSaved.Model = existingModel ??
                    new Model
                    {
                        Name = car.Model.Name
                    }

                //Car -> Model -> Manufacturer
                if(car.Model.Manufacturer != null)
                {
                    Manufacturer existingManufacturer = 
                        context.Manufacturers
                               .FirstOrDefault(x => x.Name == car.Model
                                                                 .Manufacturer
                                                                 .Name);

                    //If exists in DB assign, if not deep copy
                    carToBeSaved.Model.Manufacturer = existingManufacturer ??
                    new Manufacturer
                        {
                            Name = car.Model.Manufacturer.Name
                        }
                }
            }

            //Mark the Car for Addition to the DB
            context.Cars.AddObject(car);
            context.SaveChanges();

        }
    }
}

Если у кого-то есть какие-то мысли по этому поводу, поделитесь им.

Спасибо.

4b9b3361

Ответ 1

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

car.TraderId = existingTrader.Id;

В течение некоторого времени это казалось мне взломом, но в апрельской версии MSDN mag я читал, что Джулия Лерман также рекомендует этот подход.

Ответ 2

После нескольких дней возиться мне удалось придумать обходной путь, который работал у меня.

Кажется, что автомобиль, который передается CarRepository.Save(), имеет какой-то внутренний контекст, который является undetectable... Это так, это невозможно отделить его от этого контекста /s и добавить его в файл CarRepository.Save(). Чтобы действительно добавить его в этот контекст, я глубоко/лениво копирую объект Car и его навигационные свойства, если они есть.


Обходной путь

public void Save(Car car)
{
    using (DBContext context = new DBContext())
    {
        // No need to save if it already exists
        if ( context.Cars
                    .Any(x => x.RegistrationNumber == car.RegistrationNumber))
        {
            return;
        }
        else
        {
            //Assign scalar properties to the deep copy
            Car carToBeSaved = new Car 
            {
                carToBeSaved.RegistrationNumber = car.RegistrationNumber,
                carToBeSaved.Price = car.Price
            }


            //Car -> Trader -> ...
            if(car.Trader != null)
            {   
                Trader existingTrader = 
                    context.Traders
                           .FirstOrDefault(x => x.Name == car.Trader.Name);

                //If exists in DB assign, if not deep copy
                carToBeSaved.Trader = existingTrader ??
                    new Trader
                    {
                        Name = car.Trader.Name,
                        JobTitle = car.Trader.JobTitle
                    }

                //Car -> Trader -> TraderCompany
                if(car.Trader.TraderCompany != null)
                {
                    TraderCompany existingTraderCompany = 
                        context.TradersCompanys
                               .FirstOrDefault(x => x.Name == car.Trader
                                                                 .TraderCompany
                                                                 .Name);

                    //If exists in DB assign, if not deep copy  
                    carToBeSaved.Trader.TraderCompany = existingTraderCompany ??
                        new TraderCompany
                        {
                            Name = car.Trader.TraderCompany.Name,
                            Address = car.Trader.TraderCompany.Address,
                            PhoneNumber = car.Trader.TraderCompany.PhoneNumber
                        }
                }
            }

            //Car -> Model -> ...
            if(car.Model != null)
            {   
                Model existingModel = 
                    context.Models
                           .FirstOrDefault(x => x.Name == car.Model.Name);

                //If exists in DB assign, if not deep copy
                carToBeSaved.Model = existingModel ??
                    new Model
                    {
                        Name = car.Model.Name
                    }

                //Car -> Model -> Manufacturer
                if(car.Model.Manufacturer != null)
                {
                    Manufacturer existingManufacturer = 
                        context.Manufacturers
                               .FirstOrDefault(x => x.Name == car.Model
                                                                 .Manufacturer
                                                                 .Name);

                    //If exists in DB assign, if not deep copy
                    carToBeSaved.Model.Manufacturer = existingManufacturer ??
                    new Manufacturer
                        {
                            Name = car.Model.Manufacturer.Name
                        }
                }
            }

            //Mark the Car for Addition to the DB
            context.Cars.AddObject(car);
            context.SaveChanges();

        }
    }
}

Если у кого-то есть какие-то мысли по этому поводу, поделитесь им.

Ответ 3

Я думаю, у вас есть следующая проблема:

Если вы установили Model в existingModel, как здесь,

if (existingModel != null)
{
    car.Model = existingModel;
    Assert.IsTrue(context.ObjectStateManager.GetObjectStateEntry(car.Model).State == EntityState.Unchanged);
}

то ObjectStateManager обнаруживает, что автомобиль является новым и автоматически добавляет его в ваш контекст. Из-за этого EF обнаруживает, что, например, заданный автомобиль. Трейдер неизвестен (означает новый) и добавляет его также. И так далее.

Вот почему тезисы будут сохранены в любом случае.