如何设置一个集合财产FKS? [英] How to set a collection-property with FKs?

查看:182
本文介绍了如何设置一个集合财产FKS?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个商业类别模式。

每个商业有许多类别通过暴露的集合(类别是不顾商业实体)

Each Business has many Categories via an exposed collection (Category is disregarding the Business entity).

现在,这里是我的控制器动作:

Now here is my controller-action:

[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 设置为一个现有的类别(即标题 类别缺少寿)的属性。

Now there are several business.Categories that have their CategoryId set to an existing Category (the Title property of Category is missing tho).

击中后的SaveChanges 和重装商业从服务器中,类别不存在。

After hitting SaveChanges and reloading the Business from server, the Categories are not there.

所以我的问题是什么是设置 Business.Categories 与现有的定数组中的正确方法的CategoryId 秒。

So my question is what's the proper way to set Business.Categories with a given array of existing CategoryIds.

在创建新的商业但是,下面的<$ C $当调用C> DbUpdateException 抛出异常的SaveChanges

When create a new Business however, the following DbUpdateException exception is thrown when calling SaveChanges:

同时节省不为他们的关系公开外键属性的实体时出错。该EntityEntries属性将返回null,因为一个单独的实体不能被认定为异常的来源。异常处理,同时节省可制成通过在实体类型揭露外键的属性更容易。 。详情请参阅的InnerException

An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types. See the InnerException for details.

内部异常( OptimisticConcurrencyException

商店更新,插入或删除语句影响行的意外数字(0)。实体可能已被修改或删除,因为实体被装。刷新ObjectStateManager条目。

Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries.

答案后,这里的更新代码

Update

after answer, here's the update code:

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

在保存实体时出现错误不为他们的关系公开的外键的属性。该EntityEntries属性将返回null,因为一个单独的实体不能被认定为异常的来源。异常处理,同时节省可制成通过在实体类型揭露外键的属性更容易。 。详情请参阅的InnerException

An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types. See the InnerException for details.

下面是业务/分类模式怎么看这样的:

Here's how the Business/Category models look like:

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;
    }
  }
}



只是为了让它再次明确中,类别我连接到现有的/新的商业 ES已经存在于数据库,并有一个ID号, 。是我使用与连接它什么

Just to make it clear again, the Category I'm attaching to existing/new Businesses already exist in the DB and have an ID, which is what I'm using to attach it with.

推荐答案

我对待这两种情况 - 更新现有的业务和添加新业务 - 分开,因为你所提到的两个问题有不同的原因。

I treat the two cases - updating an existing business and adding a new business - separately because the two problems you mentioned have different reasons.

这就是如果情况下(如果(business.BusinessId大于0))在你的榜样。很显然,没有任何反应这里没有任何变化将被存储到数据库中,因为你只是附加的类别对象和商业实体,然后调用的SaveChanges 。连接意味着实体将被添加到状态的背景下不变并为在该国EF不会发出任何命令到数据库的所有实体。

That's the if case (if (business.BusinessId > 0)) in your example. It is clear that nothing happens here and no change will be stored to the database because you are just attaching the Category objects and the Business entity and then call SaveChanges. Attaching means that the entities are added to the context in state Unchanged and for entities that are in that state EF won't send any command to the database at all.

如果您想更新分离对象图 - 商业加上分类收集在您的案件的实体 - 你通常会有一个集合项目可能已经从集合中删除,并可能已添加一个项目的问题 - 与存储在数据库中的当前状态。这可能是也可能是一个集合项的属性和父实体商业已被修改。除非你手动跟踪所有的变化,而对象图是超脱 - 即EF本身不能跟踪的变化 - 这是一个Web应用程序困难的,因为你不得不这样做是在浏览器的用户界面,你只能执行一个正确的UPDATE机会整个对象图是在数据库与当前状态进行比较,然后把物品放入正确的状态添加删除修改(也许不变对于其中的一些)。

If you want to update a detached object graph - Business plus collection of Category entities in your case - you generally have the problem that a collection item could have been removed from the collection and an item could have been added - compared to the current state stored in the database. It might be also possible that a collection item's properties and that the parent entity Business have been modified. Unless you have tracked all changes manually while the object graph was detached - i.e. EF itself could not track the changes - which is difficult in a web application because you had to do this in the browser UI, your only chance to perform a correct UPDATE of the whole object graph is comparing it with the current state in the database and then put the objects into the correct state Added, Deleted and Modified (and perhaps Unchanged for some of them).

因此,程序加载商业包括它的电流类别从数据库中,然后合并分离的图形到负载(=附后)图形的变化。它可能是这样的:

So, the procedure is to load the Business including its current Categories from the database and then merge the changes of the detached graph into the loaded (=attached) graph. It could look like this:

private ActionResult Save(Business business)
{
    if (business.BusinessId > 0) // = business exists
    {
        var businessInDb = Context.Businesses
            .Include(b => b.Categories)
            .Single(b => b.BusinessId == business.BusinessId);

        // Update parent properties (only the scalar properties)
        Context.Entry(businessInDb).CurrentValues.SetValues(business);

        // Delete relationship to category if the relationship exists in the DB
        // but has been removed in the UI
        foreach (var categoryInDb in businessInDb.Categories.ToList())
        {
            if (!business.Categories.Any(c =>
                c.CategoryId == categoryInDb.CategoryId))
                businessInDb.Categories.Remove(categoryInDb);
        }

        // Add relationship to category if the relationship doesn't exist
        // in the DB but has been added in the UI
        foreach (var category in business.Categories)
        {
            var categoryInDb = businessInDb.Categories.SingleOrDefault(c =>
                c.CategoryId == category.CategoryId)

            if (categoryInDb == null)
            {
                Context.Categories.Attach(category);
                businessInDb.Categories.Add(category);
            }
            // no else case here because I assume that categories couldn't have
            // have been modified in the UI, otherwise the else case would be:
            // else
            //   Context.Entry(categoryInDb).CurrentValues.SetValues(category);
        }
    }
    else
    {
        // see below
    }
    Context.SaveChanges();

    return RedirectToAction("Index");
}



添加一个新的商业实体



您的程序添加一个新的商业类别及其关联在一起是正确的。只需附上所有类别作为现有实体上下文,然后添加新的商业上下文:

Adding a new Business entity

Your procedure to add a new Business together with its related Categories is correct. Just attach all Categories as existing entities to the context and then add the new Business to the context:

foreach (var category in business.Categories)
    Context.Categories.Attach(category);
Context.Businesses.Add(business);
Context.SaveChanges();

如果所有的类别要连接真的有存在于数据库这应该毫无例外工作的重要价值。

If all Categories you are attaching really have a key value that exists in the database this should work without exception.

您的异常意味着类别具有无效键值(即它不存在于数据库中)。也许它已在从数据库的同时,或因为它没有正确发布从Web UI回被删除

Your exception means that at least one of the Categories has an invalid key value (i.e. it does not exist in the database). Maybe it has been deleted in the meantime from the DB or because it is not correctly posted back from the Web UI.

在一个独立的协会的情况下 - 这是一个关联没有FK财产 BusinessId 类别 - 你这确实 OptimisticConcurrencyException 。 (EF似乎这里假设类别已经从数据库由另一个用户被删除。)在一个外键关联的情况下 - 这是具有FK属性的关联 BusinessId 类别 - 你会得到关于违反外键约束的例外

In case of an independent association - that is an association without FK property BusinessId in Category - you get indeed this OptimisticConcurrencyException. (EF seems to assume here that the category has been deleted from the DB by another user.) In case of a foreign key association - that is an association which has a FK property BusinessId in Category - you would get an exception about a foreign key constraint violation.

如果你想避免这种情况。例外 - 如果因为其他用户删除了类别它发生其实并不是因为类别是空/ 0 ,因为它不会回传到服务器(带有隐藏输入字段解决这个问题,而不是) - 你更好的负载类别由的CategoryId 查找)从数据库中,而不是附加它们,如果不存在了忽略它,并从企业将其删除。分类集合(或重定向到一个错误页面,告知用户或类似的东西)。

If you want to avoid this exception - and if it occurs in fact because another user deleted a Category, not because the Category is empty/0 since it doesn't get posted back to the server (fix this with a hidden input field instead) - you better load the categories by CategoryId (Find) from the database instead of attaching them and if one doesn't exist anymore ignore it and remove it from the business.Categories collection (or redirect to an error page to inform the user or something like that).

这篇关于如何设置一个集合财产FKS?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆