2016-07-06 184 views
2

希望有人可以幫助我以下,因爲我完全卡住了。「System.InvalidOperationException:集合被修改」在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 

起初我以爲它與我的一個Async方法有關(可能在下一個方法已經運行時沒有及時完成)。所以我刪除了所有的異步代碼,但仍然發生異常。我也確保我自己不改變可枚舉集合。我獲取數據(只是內存數組中的數據)並將其作爲新列表返回給TableView綁定的屬性。下面是代碼片段組成的結合(這是一個很大的信息,但我想盡可能完整):

CalendarViewController:

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

    } 
} 

CalendarViewModel:

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

UPDATE:

我已經直接引用我的項目MvvmCross的調試組件和想通了,拋出異常在MvxTaskBasedBindingContext類的127行上,並且總是在第二次迭代中發生。從這我得出結論,集合在第一次迭代期間被改變。不幸的是我不知道爲什麼或如何。

我注意到MvxTaskBasedBindingContext取代MvxBindingContext(在11-5-2016由softlion更​​改)。當我強制我的應用程序使用MvxBindingContext類時,所有的都很好(雖然有點滯後)。這讓我相信問題出在MvxTaskBasedBindingContext,但我真的不知道爲什麼,任何幫助將不勝感激。

更新2:

經過一些調試和擺弄周圍,我發現該異常有關,我的CalendarTableViewCell類設置綁定(應定義在tableview中的每個項目提供佈局我的CalendarViewController。當我註釋掉CalendarTableViewCell類中的綁定時,異常不會發生(請參閱上面的代碼)。但我仍然不知道可能是錯誤的。

+0

什麼版本的MvvmCross?我認爲這樣的事情已經解決了。 – Cheesebaron

+0

我在版本4.2.0,4.2.1和4.2.2中遇到此問題。 –

+1

我對iOS不太熟悉,但我注意到的一件事是他們傾向於使用'DelayBind'。因此,也許你的MvxTableViewCell綁定包裹在'this.DelayBind(()=> {/ *你的代碼在LayoutSubviews中綁定在這裏* /});'可能有幫助嗎?純粹的猜測,我不知道iOS如何工作。 – Plac3Hold3r

回答

1

您可以使用DelayBindCalendarTableViewCell延遲綁定,直到你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(); 
     }); 
    } 
} 
1

問題設定不會被固定延遲綁定。 問題是列表枚舉在任務中,可以在枚舉進行時修改。

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()的副本。

此bug已經被報告。 Link

+0

我同意這個問題沒有用延遲綁定來解決。然而,這似乎是一個很好的解決方法(直到有人解決實際問題,這可能是我)。 –