Entity framework 在EF中保存多个子实体会导致键值冲突
我有一个配方实体,它有一组图像实体。在我的控制器中,我尝试保存一个带有两个新图像的新配方:Entity framework 在EF中保存多个子实体会导致键值冲突,entity-framework,entity-framework-4,Entity Framework,Entity Framework 4,我有一个配方实体,它有一组图像实体。在我的控制器中,我尝试保存一个带有两个新图像的新配方: _recipeService.Insert(recipe); try { foreach (Image img in recipe.Images) { _imageService.Update(img); } _recipeService.Save(); 在后台,我使用工作单元模式来确保所有服务都使用相同的DbContext,对任何服务调用Save()都将保存所有实体。因此,
_recipeService.Insert(recipe);
try
{
foreach (Image img in recipe.Images)
{
_imageService.Update(img);
}
_recipeService.Save();
在后台,我使用工作单元模式来确保所有服务都使用相同的DbContext,对任何服务调用Save()都将保存所有实体。因此,如果在创建过程中将两个图像附加到配方,则在尝试保存时会出现以下错误:
AcceptChanges cannot continue because the object's key values conflict with another object in the ObjectStateManager. Make sure that the key values are unique before calling AcceptChanges.
我确信这与两个图像实体的ID都为0这一事实有关。这里的最佳实践是什么
FWIW,这是我的首次迁移:
public override void Up()
{
CreateTable(
"dbo.Recipe",
c => new
{
ID = c.Int(nullable: false, identity: true),
CategoryId = c.Int(nullable: false),
Title = c.String(),
Summary = c.String(),
Directions = c.String(),
CookingTime = c.Int(nullable: false),
CreationDate = c.DateTime(nullable: false),
PostedDate = c.DateTime(),
LastModifiedDate = c.DateTime(nullable: false),
Visible = c.Boolean(nullable: false),
TagList = c.String(),
Ingredient_ID = c.Int(),
})
.PrimaryKey(t => t.ID)
.ForeignKey("dbo.Category", t => t.CategoryId, cascadeDelete: true)
.ForeignKey("dbo.Ingredient", t => t.Ingredient_ID)
.Index(t => t.CategoryId)
.Index(t => t.Ingredient_ID);
CreateTable(
"dbo.Image",
c => new
{
ID = c.Int(nullable: false, identity: true),
RecipeId = c.Int(nullable: false),
ImageUrl = c.String(nullable: false),
MainImage = c.Boolean(nullable: false),
})
.PrimaryKey(t => t.ID)
.ForeignKey("dbo.Recipe", t => t.RecipeId, cascadeDelete: true)
.Index(t => t.RecipeId);
谢谢
克里斯
编辑:为配方服务添加代码
使用Barbeurian.数据;
使用烤箱模型;
使用制度;
使用System.Collections.Generic;
使用System.Linq;
使用System.Web
public class RecipeService : Service<Recipe>
{
private IService<Tag> _tagService;
public RecipeService(IUnitOfWork unitOfWork, IService<Tag> tagService)
{
_unitOfWork = unitOfWork;
_tagService = tagService;
_repo = _unitOfWork.RecipeRepository;
}
public override void Insert(Recipe recipeToCreate)
{
List<Tag> tags = new List<Tag>();
foreach (Tag tag in recipeToCreate.Tags)
{
if (_tagService.Get(filter: t => t.Name == tag.Name).Count() > 0)
{
tags.Add(_tagService.Get(filter: t => t.Name == tag.Name).SingleOrDefault());
}
else
{
Tag newTag = new Tag() {
Name = tag.Name
};
_tagService.Insert(newTag);
tags.Add(newTag);
}
}
recipeToCreate.Tags = tags;
// Validation logic
if(Validate(recipeToCreate))
_repo.Insert(recipeToCreate);
}
protected override bool Validate(Recipe recipeToValidate)
{
//Ensure recipe has a unique title
if (_repo.Get(r => r.Title == recipeToValidate.Title).Count() > 0)
_validationState.AddError("Title", "That title already exists.");
return _validationState.IsValid;
}
}
公共类配方服务:服务
{
私人电视服务;
公共配方服务(IUnitOfWork、iSeries服务)
{
_unitOfWork=unitOfWork;
_tagService=tagService;
_回购=_unitOfWork.recipereposition;
}
公共覆盖无效插入(配方recipeToCreate)
{
列表标签=新列表();
foreach(recipeToCreate.Tags中的标记)
{
if(_tagService.Get(filter:t=>t.Name==tag.Name).Count()>0)
{
tags.Add(_tagService.Get(filter:t=>t.Name==tag.Name.SingleOrDefault());
}
其他的
{
Tag newTag=new Tag(){
Name=tag.Name
};
_tagService.Insert(新标签);
添加标签(newTag);
}
}
recipeToCreate.Tags=标签;
//验证逻辑
如果(验证(recipeToCreate))
_回购插入(recipeToCreate);
}
受保护的覆盖布尔验证(配方recipeToValidate)
{
//确保配方具有唯一的标题
if(_repo.Get(r=>r.Title==recipeToValidate.Title).Count()>0)
_validationState.AddError(“标题”,“该标题已存在”);
返回_validationState.IsValid;
}
}
}
这继承自:
public abstract class Service<TEntity> : IService<TEntity> where TEntity : class, IModel
{
protected IRepository<TEntity> _repo;
protected IValidationDictionary _validationState;
protected IUnitOfWork _unitOfWork;
public virtual IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = ""){
return _repo.Get(filter, orderBy, includeProperties);
}
public virtual TEntity GetByID(int id, string includeProperties = ""){
return _repo.GetByID(id, includeProperties);
}
public virtual void Insert(TEntity entity){
_repo.Insert(entity);
}
public virtual void Delete(object id){
_repo.Delete(id);
}
public virtual void Delete(TEntity entity){
_repo.Delete(entity);
}
public virtual void Update(TEntity entity)
{
_repo.Update(entity);
}
public virtual void Save()
{
_unitOfWork.Save();
}
public virtual void Dispose()
{
_unitOfWork.Dispose();
}
protected abstract bool Validate(TEntity entity);
public IValidationDictionary ValidationState
{
get
{
return _validationState;
}
set
{
_validationState = value;
}
}
}
}
公共抽象类服务:IService,其中tenty:class,IModel
{
受保护的存款回购;
受保护的IValidationDictionary验证状态;
受保护的工作单元;
公共虚拟IEnumerable Get(表达式筛选器=null,
Func orderBy=null,
字符串includeProperties=“”){
return _repo.Get(过滤器、orderBy、includeProperties);
}
公共虚拟tenty GetByID(int-id,字符串includeProperties=“”){
返回_repo.GetByID(id,包括属性);
}
公共虚拟空白插入(TEntity实体){
_回购插入(实体);
}
公共虚拟无效删除(对象id){
_回购协议删除(id);
}
公共虚拟无效删除(TEntity实体){
_回购。删除(实体);
}
公共虚拟无效更新(TEntity实体)
{
_回购更新(实体);
}
公共虚拟void Save()
{
_unitOfWork.Save();
}
公共虚拟void Dispose()
{
_unitOfWork.Dispose();
}
受保护的抽象布尔验证(TEntity实体);
公共发票验证状态
{
得到
{
返回_validationState;
}
设置
{
_验证状态=值;
}
}
}
}
可能在添加新图像时调用insert显示\u Recipes服务的实现您是否尝试使用锁来确保您没有以线程方式调用Save,从而导致竞争条件?另外,您真的需要调用所有图像的更新吗?EF不应该遍历树并找到它需要更新的所有实体吗?我希望我可以保持整个操作的事务性。如果一次插入失败,我不想提交操作的任何部分。工作单元保证你可以做任何你想做的事情,只有你调用save操作才会提交Groan。。。我没有注意到我正在调用Update(),而我本应该调用Insert()。谢谢你的到场。