"System.InvalidOperationException: Collection was modified" in MvvmCross TableView 绑定



希望有人可以帮助我解决以下问题,因为我完全陷入困境。

当我绑定我的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

起初我认为它与我的一个异步方法有关(当下一个方法已经在运行时,它可能无法及时完成)。所以我删除了所有异步代码,但异常仍然发生。我还确保我自己不会更改可枚举集合。我获取数据(它只是一个内存数组),并将其作为新列表返回到 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);
}

日历表视图单元格:

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 类的第 127 行抛出,并且总是在第二次迭代时发生。由此我得出结论,集合在第一次迭代期间发生了变化。不幸的是,我不知道为什么或如何。

我注意到MvxTaskBasedBindingContext取代了MvxBindingContext(由 softlion 于 11-5-2016 更改)。当我强制我的应用程序使用 MvxBindingContext 类时,一切运行良好(尽管有点滞后)。这让我相信问题出在MvxTaskBasedBindingContext但我真的不知道为什么,任何帮助将不胜感激。

更新 2:

经过更多的调试和摆弄,我发现异常与我的CalendarTableViewCell类设置的绑定有关(它应该为我的CalendarViewController中定义的表视图中的每个项目提供布局。当我注释掉 CalendarTableViewCell 类中的绑定时,不会发生异常(请参阅上面的代码)。我仍然不知道可能出了什么问题。

您可以使用CalendarTableViewCell中的DelayBind来延迟绑定,直到您的DataContext设置在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() 的副本。

此错误已报告。链接

相关内容

  • 没有找到相关文章

最新更新