我有一个 Business
和一个 Category
模型。
每个 Business
通过一个公开的集合拥有许多 Categories
(Category
不考虑 Business
实体)。
这是我的控制器操作:
[HttpPost]
[ValidateAntiForgeryToken]
private ActionResult Save(Business business)
{
//Context is a lazy-loaded property that returns a reference to the DbContext
//It's disposal is taken care of at the controller's Dispose override.
foreach (var category in business.Categories)
Context.Categories.Attach(category);
if (business.BusinessId > 0)
Context.Businesses.Attach(business);
else
Context.Businesses.Add(business);
Context.SaveChanges();
return RedirectToAction("Index");
}
现在有几个 `business.Categories` 的 `CategoryId` 被设置为现有的 `Category`(`Category` 的 `Title` 属性缺失)。
点击 `SaveChanges` 并重新从服务器加载 `Business` 后,这些 `Categories` 就不再存在了。
所以我的问题是,用给定的现有 `CategoryId` 数组设置 `Business.Categories` 的正确方法是什么?
然而,在创建新的 `Business` 时,调用 `SaveChanges` 时会抛出以下 `DbUpdateException` 异常:
“在保存不公开其关系的外键属性的实体时发生错误。由于无法将单个实体标识为异常源,因此 `EntityEntries` 属性将返回 null。通过在实体类型中公开外键属性,可以更轻松地处理保存时的异常。有关详细信息,请参见 InnerException。”
内部异常(`OptimisticConcurrencyException`):
“存储更新、插入或删除语句影响了意外数量的行(0)。自加载实体以来,可能已经修改或删除了实体。请刷新 ObjectStateManager 条目。”
更新:回答后,下面是更新代码:
var storeBusiness = IncludeChildren().SingleOrDefault(b => b.BusinessId == business.BusinessId);
var entry = Context.Entry(storeBusiness);
entry.CurrentValues.SetValues(business);
//storeBusiness.Categories.Clear();
foreach (var category in business.Categories)
{
Context.Categories.Attach(category);
storeBusiness.Categories.Add(category);
}
当调用
SaveChanges
时,我会得到以下DbUpdateException
:
这是Business / Category模型的样子:在保存不公开其关系属性的外键实体时发生错误。 EntityEntries属性将返回null,因为无法将单个实体标识为异常来源。通过在实体类型中公开外键属性,可以更轻松地处理保存时的异常。请参阅InnerException获取详细信息。
public class Business
{
public int BusinessId { get; set; }
[Required]
[StringLength(64)]
[Display(Name = "Company name")]
public string CompanyName { get; set; }
public virtual BusinessType BusinessType { get; set; }
private ICollection<Category> _Categories;
public virtual ICollection<Category> Categories
{
get
{
return _Categories ?? (_Categories = new HashSet<Category>());
}
set
{
_Categories = value;
}
}
private ICollection<Branch> _Branches;
public virtual ICollection<Branch> Branches
{
get
{
return _Branches ?? (_Branches = new HashSet<Branch>());
}
set
{
_Branches = value;
}
}
}
public class Category
{
[Key]
public int CategoryId { get; set; }
[Unique]
[Required]
[MaxLength(32)]
public string Title { get; set; }
public string Description { get; set; }
public int? ParentCategoryId { get; set; }
[Display(Name = "Parent category")]
[ForeignKey("ParentCategoryId")]
public virtual Category Parent { get; set; }
private ICollection<Category> _Children;
public virtual ICollection<Category> Children
{
get
{
return _Children ?? (_Children = new HashSet<Category>());
}
set
{
_Children = value;
}
}
}
再次明确一下,我要把现有/新的企业
附加到已经存在于数据库中并具有ID的类别
中。
ObjectStateEntry
的SetValues
是否会更新内部属性(例如Categories
)?我问这个问题是因为图形非常庞大!此外,这些类别永远不应在此页面上进行编辑。我只想用新的类别替换旧的类别,而不考虑旧的类别。 - Shimmy WeitzhandlerSetValues
只更新Business
的标量属性,而不是导航属性。你所说的“用新的类别替换旧的类别”是什么意思?我的理解是(对于更新情况):您加载一个业务和类别。然后用户可以将其他(但现有的)类别分配给业务(复选框?),或从业务中取消分配类别(取消复选框?)。这样对吗? - Slauma