【发布时间】: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 case 是 grandparent 之一 - 制造商。
-
另外两件小事: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