2012-06-18 65 views
4

模型綁定過程中我也得到一個錯誤,當我使用嘗試使用這兩個屬性的模型綁定:模型綁定錯誤

private IEnumerable<Claimant> _drivers; 
    public IEnumerable<Claimant> Drivers 
    { 
     get 
     { 
      return _drivers ?? Enumerable.Empty<Claimant>(); 
     } 
     set 
     { 
      _drivers = value; 
     } 
    } 

    private IEnumerable<Property> _vehicles; 
    public IEnumerable<Property> Vehicles 
    { 
     get 
     { 
      return _vehicles ?? Enumerable.Empty<Property>(); 
     } 
     set 
     { 
      _vehicles = value; 
     } 
    } 

錯誤:

System.Reflection.TargetInvocationException was unhandled by user code 
    Message=Exception has been thrown by the target of an invocation. 
    Source=mscorlib 
    StackTrace: 
     <snip> 
    InnerException: System.NotSupportedException 
     Message=Collection is read-only. 
     Source=mscorlib 
     StackTrace: 
      at System.SZArrayHelper.Clear[T]() 
      at System.Web.Mvc.DefaultModelBinder.CollectionHelpers 
         .ReplaceCollectionImpl[T](ICollection`1 collection, IEnumerable newContents) 
     InnerException: 

如果我將屬性更改爲基本自動屬性:

public IEnumerable<Claimant> Drivers { get; set; } 
    public IEnumerable<Property> Vehicles { get; set; } 

一切工作正常。

爲什麼模型綁定在setter與auto屬性設置器相同時會出現問題?

編輯 - 通過default model binder source閱讀最終會導致你這一點,其中,第一行是調用Clear()對財產,所以當我回到了Empty<T>這顯然是行不通的。

private static void ReplaceCollectionImpl<T>(ICollection<T> collection, IEnumerable newContents) 
{ 
    collection.Clear(); 
    if (newContents != null) 
    { 
     foreach (object item in newContents) 
     { 
      // if the item was not a T, some conversion failed. the error message will be propagated, 
      // but in the meanwhile we need to make a placeholder element in the array. 
      T castItem = (item is T) ? (T)item : default(T); 
      collection.Add(castItem); 
     } 
    } 
} 

回答

6

嘗試這樣的:

get 
{ 
    return _drivers ?? new List<Claimant>(); 
} 
+0

這糾正錯誤,它只是整個事情覺得奇怪,我。我想我將不得不深入到默認的活頁夾源代碼中,看看它在這裏做了什麼。 – asawyer

+6

屬性返回的集合必須是動態的,而不是靜態只讀數組,因爲模型聯編程序會嘗試調整其大小。 –

+0

是的就是這樣。再次感謝。 – asawyer

1

在自定義的getter,我通常看到的空合併運算符(?)用於在返回到設置專用支持字段:

private IEnumerable<Claimant> _drivers; 
public IEnumerable<Claimant> Drivers 
{ 
    get 
    { 
     return _drivers ?? (_drivers = Enumerable.Empty<Claimant>()); 
    } 
    set 
    { 
     _drivers = value; 
    } 
} 

您還可以嘗試將默認值設置爲空數組:

(_drivers = new Claimant[]{}) 
5

IIRC Enumerable.Empty<T>是一個靜態只讀枚舉,用於將空存儲不可知的枚舉傳遞給方法。它並不意味着被用作空集合的「起點」。這可能就是爲什麼你會收到錯誤。

選擇存儲機制(例如List<T>)並將其用作支持字段的類型。然後,您可以將其初始化

    類定義
  1. ,在構造函數中
  2. ,或
  3. 在第一個GET:

例子:

private List<Claimant> _drivers = new List<Claimamt>(); // Option 1 

public MyModel() 
{ 
    _drivers = new List<Claimant>(); // Option 2 
} 

public IEnumerable<Claimant> Drivers 
{ 
    get 
    { 
     return _drivers ?? (_drivers = new List<Claimant>()); // Option 3 
    } 
    set 
    { 
     _drivers = value; 
    } 
} 
+0

你是對的,先生,+1 – asawyer

+0

僅供參考,我也遇到這個問題時,默認數組爲空數組(而不是空)。解決方法是將數組設置爲null,或創建一個新的模型綁定器,如下所示(儘管我還沒有嘗試過任何一種解決方案):http://blog.baltrinic.com/software-development/dotnet/better - 陣列模型結合,在-ASP淨MVC –

1

找出導致問題的確切屬性名稱和類型:

public class TestBinder: DefaultModelBinder 
{ 
    public override object BindModel(
      ControllerContext controllerContext, 
      ModelBindingContext bindingContext) 
    { 
     Debug.WriteLine(bindingContext.ModelName); 
     Debug.WriteLine(bindingContext.ModelType.ToString()); 

     //HOW TO USE: Look at your Output for the most recently output ModelName and Type to determine where there problem lies 

     return base.BindModel(controllerContext, bindingContext); 
    } 
} 

設置此粘合劑作爲默認:

ModelBinders.Binders.DefaultBinder = new TestBinder();