我在这里找不到任何相关的答案,所以我会提前联系你:
我有一个带有2个Edit操作方法的控制器(为了更好地理解我简化了它):
MrSaleBeta01.Controllers
{
public class PostsController : Controller
{
private MrSaleDB db = new MrSaleDB();
...
// GET: Posts/Edit/5
public ActionResult Edit(int? id)
{
...
}
// POST: Posts/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit( Post post, int? CategoryIdLevel1, int? CategoryIdLevel2, int? originalCategoryId)
{
...
Category cnew = db.Categories.Find(post.CategoryId);
MoveFromCategory(post, originalCategoryId);
...
db.Entry(post).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
//move post from his old category (fromCategoryId) to a new one (post.CategoryId):
//returns true on success, false on failure.
public bool MoveFromCategory(Post post, int? fromCategoryId)
{
try
{
if (post.CategoryId == fromCategoryId)
return true;
Category cold = null, cnew = null;
if (fromCategoryId!=null)
cold = db.Categories.Find(fromCategoryId);
if (post.CategoryId != 0)
cnew = db.Categories.Find(post.CategoryId);
if (cold != null)
{
cold.Posts.Remove(post);
}
if( cnew != null)
cnew.Posts.Add(post);
db.Entry(cold).State = EntityState.Modified;
db.Entry(cnew).State = EntityState.Modified;
//db.Entry(p).State = EntityState.Modified;
//db.SaveChanges();
return true;
}
catch (Exception)
{
return false;
//throw;
}
}
}
}
所以,这个想法是非常默认的:第一个方法由Get调用并返回Edit of Edit。然后我需要通过将post对象从视图发送到HttpPost Edit方法来保存更改。
我的模型是这样的(为了更好地理解我简化了它):
MrSaleBeta01.Models
{
public class Post
{
public int Id { get; set; }
[ForeignKey("Category")]
public virtual int CategoryId { get; set; }
public virtual Category Category { get; set; }
}
public class Category
{
public Category()
{
this.Categories = new List<Category>();
this.Posts = new List<Post>();
}
#region Primitive Properties
public int CategoryId { get; set; }
public string Name { get; set; }
#endregion
#region Navigation Properties
public virtual IList<Post> Posts { get; set; }
#endregion
}
}
这个想法:每个帖子都需要拥有它的类别。每个类别可以有多个帖子或没有。 (1-N关系)。
问题:
在Edit(HttpPost)方法中,在我更新Category的对象(将Post从它的类别移动到另一个类别对象。之后我对post对象进行了一些其他修改)之后,我得到了一个错误。编辑方法:
db.Entry(post).State = EntityState.Modified;
说:
{“附加类型'MrSaleBeta01.Models.Post'的实体失败,因为同一类型的另一个实体已经具有相同的主键值。当使用'Attach'方法或设置实体的状态时,可能会发生这种情况如果图中的任何实体具有冲突的键值,则为“未更改”或“已修改”。这可能是因为某些实体是新的并且尚未收到数据库生成的键值。在这种情况下使用“添加”方法或'添加'实体状态以跟踪图表,然后根据需要将非新实体的状态设置为'未更改'或'已修改'。“}
错误是因为线路存在冲突:
cold.Posts.Remove(post);
甚至连线:
cnew.Posts.Add(post);
我尝试使用AsNoTracking()的解决方案,但没有成功, 我还尝试将行“db.Entry(post).State = EntityState.Modified”行更改为:
db.As.Attach(post)
但该行甚至无法编译。
我做错了什么?我该如何解决这个问题?
答案 0 :(得分:0)
1)您不必致电.Attach()
或.State = anything
。
您已将您的实体创建为代理对象(cold = db.Categories.Find(fromCategoryId);
),其代理职责是跟踪任何更改。例外情况说,这可能是你的问题。
2)public int CategoryId { get; set; }
应标有[Key]
(我不确定约会是否将其标记为主键,但我对此表示怀疑 - 我认为EF约定将此PK作为FK归类别,可能会混淆对象图并且行为异常......)
FromCategory
方法?我可能会忽略某些内容,但看起来只是从集合中删除Category并将其添加到另一个... EF代理会在post.CategoryId = newCatId;
之后自动为您执行此操作
<强> EDIT1:强>
4)将public virtual IList<Post> Posts { get; set; }
更改为public virtual ICollection<Post> Posts { get; set; }
<强> EDIT2:强>
1)当我根据Post模型构建PostsController时自动创建。所以我想我需要它?
3)它不仅仅是从集合中删除类别并将其添加到另一个集合中,而是将帖子从一个类别的帖子中删除到另一个类别。所以我不认为EF代理会自动执行此操作。
我不熟悉ASP,我使用桌面MVP / MVVM,所以我不确定 - 但从我的观点来看,只要你使用{你真的不需要触摸EntityState
{新实体{1}}(== var x = db.Set<X>().Create();
)(非db.X.Create();
)和其他所有内容var x = new X();
(== db.Set<X>().FetchMeWhatever();
)(否则您只获得没有代理的POCO从你的例子来看,看起来你做得对;))。
然后你有了代理实体(这就是为什么你在模型db.X.FetchMeWhatever();
上有你的引用属性 - 这个新发出的代理类型覆盖它们)并且这个代理将负责1:n,m:n,1: 1关系适合你。我认为这就是为什么人们正在使用映射器(不仅仅是EF而不仅仅是数据库映射器):)对我来说看起来像是,你试图手动完成它并且它是不必要的,它只是弄得一团糟。
代理也会处理变更跟踪(所以我说,你不需要手动设置virtual
,只是在极端情况下 - 我现在想不到任何东西......即使是并发。)< / p>
所以我的建议是:
EntityState
来引用集合ICollection<>
(正如我所说,看起来你正在这样做)var entity = new Entity();
(信任EF和他的更改跟踪器)db.Entry(x).State = EntityState.whatever;
或Category.Posts
或甚至Post.Category
无关紧要 - 让mapper完成工作。请注意,这仅适用于Post.CategoryId
引用&amp;的实体的代理类型(如上所述) id&amp; virtual
属性。 顺便说一下,有2种类型的更改跟踪,代码段和代理 - 代码段在RAM中都有原始值,并且会在ICollection<>
时进行比较,对于代理跟踪,您需要标记所有属性虚拟 - 并在x.Prop =&#34; x&#34;时间。但那是偏离主题的;)