"System.InvalidOperationException: Collection was modified" 在 MvvmCross TableView 绑定中
"System.InvalidOperationException: Collection was modified" in MvvmCross TableView binding
希望有人能帮助我解决以下问题,因为我完全被卡住了。
当我在我的 TableView 上绑定时,我在我的 MvvmCross Xamarin.iOS 应用程序中收到以下异常。这仅在我更改数据源时发生(每次更改日期时,TableView 都需要更新)。
Incident Identifier: 7E7C2B15-7CC4-4AE7-9891-C4FD82358009
CrashReporter Key: 46CC21C0-DDE1-4313-9658-EC79D767939B
Hardware Model: iPhone7,2
Process: UurwerkiOS [4326]
Path: /var/containers/Bundle/Application/75969477-A516-44C3-A5A3-5B24DDDC89C8/UurwerkiOS.app/UurwerkiOS
Identifier: com.route2it.uurwerk
Version: 1.0 (1.0.96)
Code Type: ARM-64
Parent Process: ??? [1]
Date/Time: 2016-07-04T13:16:38Z
Launch Time: 2016-07-04T13:16:31Z
OS Version: iPhone OS 9.3.2 (13F69)
Report Version: 104
Exception Type: SIGABRT
Exception Codes: #0 at 0x1816ac11c
Crashed Thread: 5
Application Specific Information:
*** Terminating app due to uncaught exception 'System.AggregateException', reason: 'System.AggregateException: A Task's exception(s) were not observed either by Waiting on the Task or accessing its Exception property. As a result, the unobserved exception was rethrown by the finalizer thread. ---> System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
at System.ThrowHelper.ThrowInvalidOperationException (ExceptionResource resource) <0x10044bec0 + 0x00024> in <filename unknown>:0
at System.Collections.Generic.List`1+Enumerator[T].MoveNextRare () <0x1003bf900 + 0x0002f> in <filename unknown>:0
at System.Collections.Generic.List`1+Enumerator[T].MoveNext () <0x1003bf830 + 0x0009f> in <filename unknown>:0
at MvvmCross.Binding.BindingContext.MvxTaskBasedBindingContext.<OnDataContextChange>b__20_0 () <0x1007c1990 + 0x0023f> in <filename unknown>:0
at System.Threading.Tasks.Task.InnerInvoke () <0x10043f1f0 + 0x0005f> in <filename unknown>:0
at System.Threading.Tasks.Task.Execute () <0x10043ea20 + 0x00043> in <filename unknown>:0
--- End of inner exception stack trace ---
---> (Inner Exception #0) System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
at System.ThrowHelper.ThrowInvalidOperationException (ExceptionResource resource) <0x10044bec0 + 0x00024> in <filename unknown>:0
at System.Collections.Generic.List`1+Enumerator[T].MoveNextRare () <0x1003bf900 + 0x0002f> in <filename unknown>:0
at System.Collections.Generic.List`1+Enumerator[T].MoveNext () <0x1003bf830 + 0x0009f> in <filename unknown>:0
at MvvmCross.Binding.BindingContext.MvxTaskBasedBindingContext.<OnDataContextChange>b__20_0 () <0x1007c1990 + 0x0023f> in <filename unknown>:0
at System.Threading.Tasks.Task.InnerInvoke () <0x10043f1f0 + 0x0005f> in <filename unknown>:0
at System.Threading.Tasks.Task.Execute () <0x10043ea20 + 0x00043> in <filename unknown>:0
起初我认为它与我的一个 Async 方法有关(可能没有及时完成,而下一个已经 运行)。所以我删除了我所有的异步代码,但异常仍然发生。我还确保自己不会更改可枚举集合。我获取数据(这只是内存数组中的一个)并将其 return 作为新列表添加到 TableView 绑定到的 属性 中。下面是构成绑定的代码片段(信息很多,但我希望尽可能完整):
日历视图控制器:
public override void ViewDidLoad()
{
base.ViewDidLoad();
if (NavigationController != null)
NavigationController.NavigationBarHidden = false;
InitCalendar();
InitNavigationItem();
InitTableView();
ApplyConstraints();
var shiftForDateTableViewSource = new MvxSimpleTableViewSource(_tableView, CalendarTableViewCell.Key, CalendarTableViewCell.Key);
shiftForDateTableViewSource.DeselectAutomatically = true;
_tableView.RowHeight = 45;
_tableView.Source = shiftForDateTableViewSource;
var set = this.CreateBindingSet<CalendarView, CalendarViewModel>();
set.Bind(shiftForDateTableViewSource).To(vm => vm.ShiftsForSelectedDate);
set.Bind(shiftForDateTableViewSource).For(vm => vm.SelectionChangedCommand).To(vm => vm.ShiftSelectedCommand);
set.Apply();
_tableView.ReloadData();
}
private void InitTableView()
{
_tableView = new UITableView();
_tableView.RegisterClassForCellReuse(typeof(UITableViewCell), CalendarTableViewCell.Key);
Add(_tableView);
}
CalendarTableViewCell:
public partial class CalendarTableViewCell : MvxTableViewCell
{
public static readonly NSString Key = new NSString("CalendarTableViewCell");
public static readonly UINib Nib;
static CalendarTableViewCell()
{
Nib = UINib.FromName("CalendarTableViewCell", NSBundle.MainBundle);
}
protected CalendarTableViewCell(IntPtr handle) : base(handle)
{
}
public override void LayoutSubviews()
{
base.LayoutSubviews();
var set = this.CreateBindingSet<CalendarTableViewCell, Shift>();
set.Bind(StartTimeLabel).To(vm => vm.StartDate).WithConversion("StringFormat", "HH:mm");
set.Bind(EndTimeLabel).To(vm => vm.EndDate).WithConversion("StringFormat", "HH:mm");
set.Bind(ColorBarView).For("BackgroundColor").To(vm => vm.Color).WithConversion("RGB");
set.Bind(TitleLabel).To(vm => vm).WithConversion("ConcatenatedEventTitle");
set.Bind(LocationLabel).To(vm => vm.Location);
set.Apply();
}
}
日历视图模型:
public class CalendarViewModel
: MvxViewModel
{
private readonly IShiftService _shiftService;
public CalendarViewModel(IShiftService shiftService)
{
if (shiftService == null)
throw new ArgumentNullException(nameof(shiftService));
_shiftService = shiftService;
}
public override void Start()
{
base.Start();
Shifts = _shiftService.GetShiftsForEmployeeAsync(1);
}
private IEnumerable<Shift> _shifts;
public IEnumerable<Shift> Shifts
{
get { return _shifts; }
set
{
SetProperty(ref _shifts,
value,
nameof(Shifts));
}
}
private IEnumerable<Shift> _shiftsForSelectedDate;
public IEnumerable<Shift> ShiftsForSelectedDate
{
get { return _shiftsForSelectedDate; }
private set
{
if (_shiftsForSelectedDate == value)
return;
SetProperty(ref _shiftsForSelectedDate,
value,
nameof(ShiftsForSelectedDate));
}
}
private DateTime? _selectedDate;
public DateTime? SelectedDate
{
get { return _selectedDate; }
set
{
if (_selectedDate == value)
return;
SetProperty(ref _selectedDate,
value,
nameof(SelectedDate));
if (_selectedDate.HasValue)
FetchShiftsForSelectedDate();
}
}
private void FetchShiftsForSelectedDate()
{
ShiftsForSelectedDate = _shiftService.GetShiftsForSelectedDateAsync(_selectedDate.Value);
}
}
MockShiftService(实现了IShiftService接口):
public class MockShiftService
: IShiftService
{
private IList<Shift> _shifts;
public MockShiftService()
{
Initialize();
}
public IEnumerable<Shift> GetShiftsForEmployeeAsync(int employeeId)
{
return _shifts;
}
public IEnumerable<Shift> GetShiftsForSelectedDateAsync(DateTime selectedDate)
{
var endDate = selectedDate.Date.Add(new TimeSpan(23, 59, 59));
return _shifts
.Where(s => s.StartDate <= endDate && s.EndDate >= selectedDate)
.ToList();
}
public Shift GetShiftByIdAsync(int shiftId)
{
return _shifts.First((shift) => shift.Id == shiftId);
}
private void Initialize()
{
var shifts = new List<Shift>();
// The in memory array gets populated here which
// is straight forward creating instances of the
// 'Shift' class and assigning it's properties before
// adding it to the 'shifts' collection. I left
// this code out to keep it as short as possible.
}
}
更新:
我已经将我的项目直接引用到 MvvmCross 的调试程序集,并发现异常是在 MvxTaskBasedBindingContext
class 的第 127 行抛出的,并且总是在第二次迭代时发生。由此我得出结论,集合在第一次迭代期间发生了变化。不幸的是,我不知道为什么或如何。
我注意到 MvxTaskBasedBindingContext
替换了 MvxBindingContext
(由 softlion 于 11-5-2016 更改)。当我强制我的应用程序使用 MvxBindingContext
class 时,一切正常(虽然有点滞后)。这让我相信问题出在 MvxTaskBasedBindingContext
但我真的不明白为什么,任何帮助将不胜感激。
更新 2:
经过更多的调试和摆弄后,我发现异常与我的 CalendarTableViewCell
class 设置的绑定有关(它应该为 tableview 中定义的每个项目提供布局我的 CalendarViewController
。当我注释掉 CalendarTableViewCell
class 中的绑定时,不会发生异常(请参阅我上面的代码)。不过我仍然不知道哪里出了问题。
您可以在 CalendarTableViewCell
中使用 DelayBind
延迟绑定,直到 DataContext
gets set on your BindingContext
public partial class CalendarTableViewCell : MvxTableViewCell
{
...
public override void LayoutSubviews()
{
base.LayoutSubviews();
this.DelayBind(() =>
{
var set = this.CreateBindingSet<CalendarTableViewCell, Shift>();
set.Bind(StartTimeLabel).To(vm => vm.StartDate).WithConversion("StringFormat", "HH:mm");
set.Bind(EndTimeLabel).To(vm => vm.EndDate).WithConversion("StringFormat", "HH:mm");
set.Bind(ColorBarView).For("BackgroundColor").To(vm => vm.Color).WithConversion("RGB");
set.Bind(TitleLabel).To(vm => vm).WithConversion("ConcatenatedEventTitle");
set.Bind(LocationLabel).To(vm => vm.Location);
set.Apply();
});
}
}
延迟绑定无法解决此问题。
问题在于列表是在任务中枚举的,可以在枚举进行时对其进行修改。
Task.Run(() =>
{
foreach (var binding in this._viewBindings)
{
foreach (var bind in binding.Value)
{
bind.Binding.DataContext = this._dataContext;
}
}
foreach (var binding in this._directBindings)
{
binding.Binding.DataContext = this._dataContext;
}
});
枚举之前需要创建集合 ToList() 或 ToArray() 的副本。
此错误已被报告。
Link
希望有人能帮助我解决以下问题,因为我完全被卡住了。
当我在我的 TableView 上绑定时,我在我的 MvvmCross Xamarin.iOS 应用程序中收到以下异常。这仅在我更改数据源时发生(每次更改日期时,TableView 都需要更新)。
Incident Identifier: 7E7C2B15-7CC4-4AE7-9891-C4FD82358009
CrashReporter Key: 46CC21C0-DDE1-4313-9658-EC79D767939B
Hardware Model: iPhone7,2
Process: UurwerkiOS [4326]
Path: /var/containers/Bundle/Application/75969477-A516-44C3-A5A3-5B24DDDC89C8/UurwerkiOS.app/UurwerkiOS
Identifier: com.route2it.uurwerk
Version: 1.0 (1.0.96)
Code Type: ARM-64
Parent Process: ??? [1]
Date/Time: 2016-07-04T13:16:38Z
Launch Time: 2016-07-04T13:16:31Z
OS Version: iPhone OS 9.3.2 (13F69)
Report Version: 104
Exception Type: SIGABRT
Exception Codes: #0 at 0x1816ac11c
Crashed Thread: 5
Application Specific Information:
*** Terminating app due to uncaught exception 'System.AggregateException', reason: 'System.AggregateException: A Task's exception(s) were not observed either by Waiting on the Task or accessing its Exception property. As a result, the unobserved exception was rethrown by the finalizer thread. ---> System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
at System.ThrowHelper.ThrowInvalidOperationException (ExceptionResource resource) <0x10044bec0 + 0x00024> in <filename unknown>:0
at System.Collections.Generic.List`1+Enumerator[T].MoveNextRare () <0x1003bf900 + 0x0002f> in <filename unknown>:0
at System.Collections.Generic.List`1+Enumerator[T].MoveNext () <0x1003bf830 + 0x0009f> in <filename unknown>:0
at MvvmCross.Binding.BindingContext.MvxTaskBasedBindingContext.<OnDataContextChange>b__20_0 () <0x1007c1990 + 0x0023f> in <filename unknown>:0
at System.Threading.Tasks.Task.InnerInvoke () <0x10043f1f0 + 0x0005f> in <filename unknown>:0
at System.Threading.Tasks.Task.Execute () <0x10043ea20 + 0x00043> in <filename unknown>:0
--- End of inner exception stack trace ---
---> (Inner Exception #0) System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
at System.ThrowHelper.ThrowInvalidOperationException (ExceptionResource resource) <0x10044bec0 + 0x00024> in <filename unknown>:0
at System.Collections.Generic.List`1+Enumerator[T].MoveNextRare () <0x1003bf900 + 0x0002f> in <filename unknown>:0
at System.Collections.Generic.List`1+Enumerator[T].MoveNext () <0x1003bf830 + 0x0009f> in <filename unknown>:0
at MvvmCross.Binding.BindingContext.MvxTaskBasedBindingContext.<OnDataContextChange>b__20_0 () <0x1007c1990 + 0x0023f> in <filename unknown>:0
at System.Threading.Tasks.Task.InnerInvoke () <0x10043f1f0 + 0x0005f> in <filename unknown>:0
at System.Threading.Tasks.Task.Execute () <0x10043ea20 + 0x00043> in <filename unknown>:0
起初我认为它与我的一个 Async 方法有关(可能没有及时完成,而下一个已经 运行)。所以我删除了我所有的异步代码,但异常仍然发生。我还确保自己不会更改可枚举集合。我获取数据(这只是内存数组中的一个)并将其 return 作为新列表添加到 TableView 绑定到的 属性 中。下面是构成绑定的代码片段(信息很多,但我希望尽可能完整):
日历视图控制器:
public override void ViewDidLoad()
{
base.ViewDidLoad();
if (NavigationController != null)
NavigationController.NavigationBarHidden = false;
InitCalendar();
InitNavigationItem();
InitTableView();
ApplyConstraints();
var shiftForDateTableViewSource = new MvxSimpleTableViewSource(_tableView, CalendarTableViewCell.Key, CalendarTableViewCell.Key);
shiftForDateTableViewSource.DeselectAutomatically = true;
_tableView.RowHeight = 45;
_tableView.Source = shiftForDateTableViewSource;
var set = this.CreateBindingSet<CalendarView, CalendarViewModel>();
set.Bind(shiftForDateTableViewSource).To(vm => vm.ShiftsForSelectedDate);
set.Bind(shiftForDateTableViewSource).For(vm => vm.SelectionChangedCommand).To(vm => vm.ShiftSelectedCommand);
set.Apply();
_tableView.ReloadData();
}
private void InitTableView()
{
_tableView = new UITableView();
_tableView.RegisterClassForCellReuse(typeof(UITableViewCell), CalendarTableViewCell.Key);
Add(_tableView);
}
CalendarTableViewCell:
public partial class CalendarTableViewCell : MvxTableViewCell
{
public static readonly NSString Key = new NSString("CalendarTableViewCell");
public static readonly UINib Nib;
static CalendarTableViewCell()
{
Nib = UINib.FromName("CalendarTableViewCell", NSBundle.MainBundle);
}
protected CalendarTableViewCell(IntPtr handle) : base(handle)
{
}
public override void LayoutSubviews()
{
base.LayoutSubviews();
var set = this.CreateBindingSet<CalendarTableViewCell, Shift>();
set.Bind(StartTimeLabel).To(vm => vm.StartDate).WithConversion("StringFormat", "HH:mm");
set.Bind(EndTimeLabel).To(vm => vm.EndDate).WithConversion("StringFormat", "HH:mm");
set.Bind(ColorBarView).For("BackgroundColor").To(vm => vm.Color).WithConversion("RGB");
set.Bind(TitleLabel).To(vm => vm).WithConversion("ConcatenatedEventTitle");
set.Bind(LocationLabel).To(vm => vm.Location);
set.Apply();
}
}
日历视图模型:
public class CalendarViewModel
: MvxViewModel
{
private readonly IShiftService _shiftService;
public CalendarViewModel(IShiftService shiftService)
{
if (shiftService == null)
throw new ArgumentNullException(nameof(shiftService));
_shiftService = shiftService;
}
public override void Start()
{
base.Start();
Shifts = _shiftService.GetShiftsForEmployeeAsync(1);
}
private IEnumerable<Shift> _shifts;
public IEnumerable<Shift> Shifts
{
get { return _shifts; }
set
{
SetProperty(ref _shifts,
value,
nameof(Shifts));
}
}
private IEnumerable<Shift> _shiftsForSelectedDate;
public IEnumerable<Shift> ShiftsForSelectedDate
{
get { return _shiftsForSelectedDate; }
private set
{
if (_shiftsForSelectedDate == value)
return;
SetProperty(ref _shiftsForSelectedDate,
value,
nameof(ShiftsForSelectedDate));
}
}
private DateTime? _selectedDate;
public DateTime? SelectedDate
{
get { return _selectedDate; }
set
{
if (_selectedDate == value)
return;
SetProperty(ref _selectedDate,
value,
nameof(SelectedDate));
if (_selectedDate.HasValue)
FetchShiftsForSelectedDate();
}
}
private void FetchShiftsForSelectedDate()
{
ShiftsForSelectedDate = _shiftService.GetShiftsForSelectedDateAsync(_selectedDate.Value);
}
}
MockShiftService(实现了IShiftService接口):
public class MockShiftService
: IShiftService
{
private IList<Shift> _shifts;
public MockShiftService()
{
Initialize();
}
public IEnumerable<Shift> GetShiftsForEmployeeAsync(int employeeId)
{
return _shifts;
}
public IEnumerable<Shift> GetShiftsForSelectedDateAsync(DateTime selectedDate)
{
var endDate = selectedDate.Date.Add(new TimeSpan(23, 59, 59));
return _shifts
.Where(s => s.StartDate <= endDate && s.EndDate >= selectedDate)
.ToList();
}
public Shift GetShiftByIdAsync(int shiftId)
{
return _shifts.First((shift) => shift.Id == shiftId);
}
private void Initialize()
{
var shifts = new List<Shift>();
// The in memory array gets populated here which
// is straight forward creating instances of the
// 'Shift' class and assigning it's properties before
// adding it to the 'shifts' collection. I left
// this code out to keep it as short as possible.
}
}
更新:
我已经将我的项目直接引用到 MvvmCross 的调试程序集,并发现异常是在 MvxTaskBasedBindingContext
class 的第 127 行抛出的,并且总是在第二次迭代时发生。由此我得出结论,集合在第一次迭代期间发生了变化。不幸的是,我不知道为什么或如何。
我注意到 MvxTaskBasedBindingContext
替换了 MvxBindingContext
(由 softlion 于 11-5-2016 更改)。当我强制我的应用程序使用 MvxBindingContext
class 时,一切正常(虽然有点滞后)。这让我相信问题出在 MvxTaskBasedBindingContext
但我真的不明白为什么,任何帮助将不胜感激。
更新 2:
经过更多的调试和摆弄后,我发现异常与我的 CalendarTableViewCell
class 设置的绑定有关(它应该为 tableview 中定义的每个项目提供布局我的 CalendarViewController
。当我注释掉 CalendarTableViewCell
class 中的绑定时,不会发生异常(请参阅我上面的代码)。不过我仍然不知道哪里出了问题。
您可以在 CalendarTableViewCell
中使用 DelayBind
延迟绑定,直到 DataContext
gets set on your BindingContext
public partial class CalendarTableViewCell : MvxTableViewCell
{
...
public override void LayoutSubviews()
{
base.LayoutSubviews();
this.DelayBind(() =>
{
var set = this.CreateBindingSet<CalendarTableViewCell, Shift>();
set.Bind(StartTimeLabel).To(vm => vm.StartDate).WithConversion("StringFormat", "HH:mm");
set.Bind(EndTimeLabel).To(vm => vm.EndDate).WithConversion("StringFormat", "HH:mm");
set.Bind(ColorBarView).For("BackgroundColor").To(vm => vm.Color).WithConversion("RGB");
set.Bind(TitleLabel).To(vm => vm).WithConversion("ConcatenatedEventTitle");
set.Bind(LocationLabel).To(vm => vm.Location);
set.Apply();
});
}
}
延迟绑定无法解决此问题。 问题在于列表是在任务中枚举的,可以在枚举进行时对其进行修改。
Task.Run(() =>
{
foreach (var binding in this._viewBindings)
{
foreach (var bind in binding.Value)
{
bind.Binding.DataContext = this._dataContext;
}
}
foreach (var binding in this._directBindings)
{
binding.Binding.DataContext = this._dataContext;
}
});
枚举之前需要创建集合 ToList() 或 ToArray() 的副本。
此错误已被报告。 Link