【问题标题】:Collection was modified when adding to collection添加到集合时修改了集合
【发布时间】:2013-08-11 04:45:54
【问题描述】:

尝试添加到集合时出现“集合已修改”异常

public void UpdateLinks(EventViewModel form)
{
    var selectedIds = form.Links.Select(r => r.ResourceTypeID).ToList();
    var assignedIds = form.Event.Links.Select(r => r.ResourceTypeID).ToList();
    foreach (var resource in form.Links)
    {
        resource.EventID = form.Event.ID;
        if (!assignedIds.Contains(resource.ResourceTypeID))
            form.Event.Links.Add(resource);
    }
    foreach (var resource in form.Event.Links.ToList())
    {
        if (!selectedIds.Contains(resource.ResourceTypeID))
            form.Event.Links.Remove(resource);
    }
}

问题出在“添加”方法上。如果我将那部分注释掉,则不会引发异常。需要注意的是,我已经尝试将 foreach 重写为 for 循环并将“ToList()”添加到 form.Links。在所有情况下都会引发相同的异常。我在网站的其他部分使用这个确切的模式没有问题,这就是为什么这如此令人沮丧。这也适用于“创建”。该问题仅影响“编辑”操作。

其他相关代码:

[HttpPost]
public ActionResult Edit(EventViewModel form, HttpPostedFileBase[] eventFiles)
{
    if (ModelState.IsValid)
    {
        eventsService.UpdateEvent(form.Event);
        eventsService.UpdateManufacturerTags(form);
        eventsService.UpdateFiles(form, eventFiles);
        eventsService.UpdateLinks(form);
        eventsService.Save();
        return RedirectToAction("Details", new { id = form.Event.ID });
    }
    return View(form);
}

public class EventViewModel : ContentLeftViewModel
{
    public Event Event { get; set; }
    public string[] SelectedManufacturers { get; set; }
    public MultiSelectList Manufacturers { get; set; }
    public IList<EventResource> Files { get; set; }
    public IList<EventResource> Links { get; set; }

    public EventViewModel()
    {
        SelectedManufacturers = new string[0];
        Files = new List<EventResource>();
        Links = new List<EventResource>();
    }
}

public class Event
{
    [Key]
    public int ID { get; set; }

    [Required]
    public string Title { get; set; }

    [Required]
    [DisplayName("Start Time")]
    [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:M/d/yyyy h:mm tt}")]
    public DateTime? StartTime { get; set; }

    [Required]
    [DisplayName("End Time")]
    [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:M/d/yyyy h:mm tt}")]
    public DateTime? EndTime { get; set; }

    public string Venue { get; set; }

    public string Address { get; set; }

    public string City { get; set; }

    public string State { get; set; }

    public string Zip { get; set; }

    [AllowHtml]
    [DataType(DataType.MultilineText)]
    public string Description { get; set; }

    [DisplayName("Registration Link")]
    public string RegistrationUrl { get; set; }

    public virtual IList<Manufacturer> Manufacturers { get; set; }

    public virtual IList<EventResource> Files { get; set; }

    public virtual IList<EventResource> Links { get; set; }

    //public IEnumerable<EventResource> Resources
    //{
    //    get { return Files.Concat(Links); }
    //}

    public string StartDate
    {
        get { return StartTime.Value.ToShortDateString(); }
    }

    public string StartTimeOnly
    {
        get { return StartTime.Value.ToShortTimeString(); }
    }

    public string EndDate
    {
        get { return EndTime.Value.ToShortDateString(); }
    }

    public string EndTimeOnly
    {
        get { return EndTime.Value.ToShortTimeString(); }
    }

    public Event()
    {
        Manufacturers = new List<Manufacturer>();
        Files = new List<EventResource>();
        Links = new List<EventResource>();
    }
}

public class EventResource
{
    [Key, Column(Order = 0)]
    public int EventID { get; set; }

    [Key, Column(Order = 1)]
    public int ResourceTypeID { get; set; }

    [Key, Column(Order = 2)]
    public string Path { get; set; }

    public virtual Event Event { get; set; }

    public virtual ResourceType Type { get; set; }
}

更新

更多信息:完全添加到集合中......即使在循环之外也会引发相同的错误。这会给任何人一个想法吗?

【问题讨论】:

  • 不建议在迭代foreach时修改集合。看看一个简单的for循环是否符合目的
  • 如果您在迭代时尝试修改它,每个 .Net 集合都会抛出异常 - 无论它是 for-each 循环还是您获得了迭代器并且正在执行它“通过手”(在 for 或 while 循环中)。尝试使用上面建议的 For-Loop,或者创建一个临时集合,其中包含您以后要删除或添加的数据。
  • 在网站的其他部分,我已经成功地对不同类型的实体使用了相同的解决方案。您是对的,但是在其他情况下,添加“ToList()”来创建一个新列表以避免该问题就足够了。我将尝试以下建议。
  • 另外,如原帖所述,我已经尝试将 foreach 重写为一个简单的 for 循环。无论哪种方式,我都会遇到相同的异常。
  • 还想强调代码原样在 Create 上完美运行。以这种方式删除项目也适用于编辑。问题出在在 Edit 上执行时此方法的 add 部分。还有其他想法吗?

标签: c# asp.net-mvc entity-framework collections


【解决方案1】:

试试这个:

var lsEvents = form.Event.Links.ToList();
foreach (var resource in form.Links)
{
    resource.EventID = form.Event.ID;
    if (!assignedIds.Contains(resource.ResourceTypeID))
        lsEvents.Add(resource);
}
foreach (var resource in form.Event.Links)
{
    if (!selectedIds.Contains(resource.ResourceTypeID))
        lsEvents.Remove(resource);
}

并根据需要使用lsEvents。这将解决您的问题。

【讨论】:

  • 使用此方法,如果您确实希望将更改反映在模型中,则还需要将 form.Event.Links 设置为 lsEvents。一旦你这样做了,同样的异常就会被抛出。非常坚持这一点!
【解决方案2】:

您可以在添加条目之前使用 LINQ 过滤掉条目。

public void UpdateLinks(EventViewModel form)
{
    var selectedIds = form.Links.Select(r => r.ResourceTypeID).ToArray();
    var assignedIds = form.Event.Links.Select(r => r.ResourceTypeID).ToArray();
    foreach (var resource in form.Links
        .Where(r=> !assignedIds.Contain(r.ResourceTypeID)).ToArray())
    {
        resource.EventID = form.Event.ID;
        form.Event.Links.Add(resource);
    }
    foreach (var resource in form.Event.Links
        .Where(r=> !selectedIds.Contain(r.ResourceTypeID)).ToArray())
    {
        form.Event.Links.Remove(resource);
    }
}

在这两种方法中,我们都是在枚举和添加之前过滤资源。不能同时枚举和添加。

【讨论】:

  • 试一试,但抛出了同样的异常
  • 现在尝试将 ToList 更改为 ToArray
  • 现在怎么样?我已将每个 To List 设置为 To Array。什么是形式。链接的类型?是列表还是其他集合类型?
  • form 是一个 EventViewModel ,它包含一个事件模型对象和其他集合,用于将发布的表单集合值与当前集合值进行比较,以便可以进行更新以说明添加到集合中的那些项目和删除的那些项目从他们。例如,form.Links 包含应该修改哪些 form.Event.Links 以匹配。
  • 不幸的是,最新的更改也没有产生任何影响。无论您将临时集合设为数组还是列表都无关紧要,所以我并不惊讶它没有任何区别,但我还是想尝试一下。这非常令人困惑,因为就像我在原始帖子中提到的那样,我已将这种确切的集合更新模式与网站上的其他实体一起使用,没有问题。
【解决方案3】:

您不能修改正在枚举的集合(例如,对于每个集合)。

您需要循环一次以获取要添加和/或删除的项目,然后在第一个循环之外的第二个循环中添加或删除所有项目。

例如:

Dim coll = New List(of String)({"1", "2", "3", "4", "6"})
dim coll2 = New List(of String)({"5", "8", "9", "2"})

Dim removeItems as new list(of String)()

For Each item in coll
 For Each item2 in coll2
  If item2 = item
    removeItems.Add(item)
  end if
 Next item2
Next item

' remove the items gathered
For each itemToRemove in removeItems
 coll.Remove(itemToRemove)
Next itemToRemove

它可以做得更好,但这显示了错误的要点。您无法更改正在循环的集合。

【讨论】:

  • 试过这个和同样的 foreach (var resource in form.Links) { resource.EventID = form.Event.ID; if (!assignedIds.Contains(resource.ResourceTypeID)) addedLinks.Add(resource); } foreach (var resource in form.Event.Links.ToList()) { if (!selectedIds.Contains(resource.ResourceTypeID)) removedLinks.Add(resource); } removedLinks.ForEach(r => form.Event.Links.Remove(r)); addedLinks.ForEach(r => form.Event.Links.Add(r));
【解决方案4】:

不确定解决问题的确切原因,但在升级到 Visual Studio 2013 Express(从 2010 Professional)并安装 ASP.Net 4.5/IIS8 后,即使此应用程序继续以 ASP.Net 4.0 为目标,我不再遇到此问题,并且原始帖子中使用的代码按原样运行。

这可能是由 ASP.Net 框架的某个版本或旧版本的 IIS 引起的?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-08-11
    • 2012-04-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-08
    • 2013-05-14
    相关资源
    最近更新 更多