简体   繁体   English

添加到收藏夹时,收藏夹已被修改

[英]Collection was modified when adding to collection

Getting the "Collection was modified" exception when attempting to add to a collection 尝试添加到集合时出现“集合已被修改”异常

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

The problem is specifically with the "Add" method. 该问题特别是与“添加”方法有关。 If I comment that part out, no exception is thrown. 如果我将该部分注释掉,则不会引发任何异常。 It's important to note that I've already tried re-writing the foreach as a for loop and adding "ToList()" to form.Links. 重要的是要注意,我已经尝试将foreach重写为for循环,并将“ ToList()”添加到form.Links中。 The same exception is thrown in all cases. 在所有情况下都会引发相同的异常。 I use this exact pattern on other parts of the site without issue which is why this is so frustrating. 我在网站的其他部分使用了这种精确的模式,没有问题,这就是为什么如此令人沮丧。 This also works on "Create". 这也适用于“创建”。 The problem only affects the "Edit" action. 该问题仅影响“编辑”操作。

Other relevant code: 其他相关代码:

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

UPDATE 更新

Some more info: Adding to the collection at all... even outside of a loop throws the same error. 更多信息:完全添加到集合中...即使在循环之外也会引发相同的错误。 Does that give anyone an idea? 这给任何人一个主意吗?

Try this instead: 尝试以下方法:

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

and Use lsEvents according to the requirement. 并根据需要使用lsEvents This will fix your issue. 这样可以解决您的问题。

You can use LINQ to filter out entries before adding them. 您可以在添加条目之前使用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);
    }
}

In both methods, we are filtering resources before enumerating and adding. 在这两种方法中,我们都在枚举和添加之前对资源进行过滤。 You can not enumerate and add at the same time. 您不能同时枚举和添加。

You can't modify a collection you are enumerating (eg, for each). 您不能修改要枚举的集合(例如,每个集合)。

You need to loop once to get the items you want to add and/or remove, then add or remove all of them in a second loop outside the first. 您需要循环一次以获取要添加和/或删除的项目,然后在第一个循环之外的第二个循环中添加或删除所有它们。

Eg: 例如:

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

It can be done a better way, but this shows the gist of the error. 可以做一个更好的方法,但这可以显示错误的要点。 You can't change the collection you are looping over. 您无法更改正在循环的集合。

Not sure exactly what was responsible for solving the issue, but after upgrading to Visual Studio 2013 Express (from 2010 Professional), and installing ASP.Net 4.5/IIS8 with it, even though this application continues to target ASP.Net 4.0, I am no longer experiencing the issue and the code used in the original post works as is. 不知道到底是什么引起了该问题,但是升级到Visual Studio 2013 Express(从2010 Professional开始)并安装了ASP.Net 4.5 / IIS8之后,即使此应用程序继续以ASP.Net 4.0为目标,我仍然不再遇到问题,原始帖子中使用的代码照常工作。

Perhaps this was caused by a certain build of the ASP.Net framework or an older version of IIS? 也许这是由于ASP.Net框架的某个版本或IIS的较早版本引起的?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM