【问题标题】:Parent object is in EntityState.Unchanged, but it still inserted in the Database父对象位于 EntityState.Unchanged 中,但仍插入到数据库中
【发布时间】:2012-10-16 17:15:10
【问题描述】:

我有一个简单的雪花模式,从中生成了我的实体框架模型。
问题是我试图将子实体映射到 现有的父和/或祖父 实体,但它仍然插入它。

我跟着这个:

Insert new object with existing object
Prevent Entity Framework to Insert Values for Navigational Properties

有趣的是,即使父实体的 EntityState 是“未更改”,实体框架仍会尝试插入它。


架构


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() 的 Car 具有某种 内部上下文,这是 无法检测到的 em>... 既然如此,不可能将其与该上下文分离并将其添加到 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();

        }
    }
}

如果有人对此有任何想法,请分享。

谢谢。

【问题讨论】:

  • 这个otherCostRecord实际上是car,对吧?您能否举例说明您插入了四个existingXXX != null 案例中的哪一个?在我看来,代码中有很多多余的东西:AddObject 下方的 ChangeObjectState 是多余的,直到 SaveChanges 之前的整个块也是多余的。我不认为这是问题的原因,但谁知道...
  • 感谢您发现那个,我编辑了它。发生插入的确切 != null casegrandparent 之一 - 制造商。
  • 另外两件小事:1) 有没有理由为什么你禁用延迟加载那么晚而不是在using 块的开头?当您检查 existingXXX 实体时,它在发布时不应该是问题,但在调试模式下,您可能会意外触发延迟加载。 2) 不要使用FirstOrDefault 加载实体并测试null 只是为了检查是否存在。您可以使用 if (context.Cars.Any(x => x.RegistrationNumber == car.RegistrationNumber)) 代替,它不会实现完整的实体。
  • 删除了我的答案。它无法工作。我错过了您不查询主键而是查询Name 属性。
  • 是 EF4 吗?可以这样:support.microsoft.com/kb/2390624?wa=wsignin1.0

标签: c# entity-framework entity-relationship


【解决方案1】:

经过几天的修补,我设法想出了一个适合我的解决方法

似乎传递给 CarRepository.Save() 的 Car 具有某种 内部上下文,这是 无法检测到的 em>... 既然如此,不可能将其与该上下文分离并将其添加到 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();

        }
    }
}

如果有人对此有任何想法,请分享。

【讨论】:

    【解决方案2】:

    我认为你有以下问题:

    如果您将模型设置为 existingModel 就像这里

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

    然后 ObjectStateManager 检测到汽车是新的,并自动将其添加到您的上下文中。由于这个 EF 检测到,例如预设的 car.Trader 是未知的(意味着新的)并且也添加了它。等等。

    这就是为什么这些论文无论如何都会被保存的原因。

    【讨论】:

      【解决方案3】:

      我发现解决此类问题的最可靠方法是使用现有对象的 ID,而不是关联实体实例。因此,在找到现有交易者后,您可以像这样为交易者设置“外来”键:

      car.TraderId = existingTrader.Id;
      

      有一段时间,这对我来说似乎是一种 hack,但在 2013 年 4 月的 MSDN 杂志中,我读到 Julie Lerman 也推荐这种方法。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-01-24
        • 1970-01-01
        • 2023-03-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-19
        相关资源
        最近更新 更多