与自定义属性getter的模型绑定错误

时间:2012-06-18 15:00:22

标签: c# asp.net-mvc-3

当我尝试使用这两个属性绑定模型时,我在模型绑定期间遇到错误:

    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属性setter相同时,为什么模型绑定会出现问题?

修改 - 通过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);
        }
    }
}

4 个答案:

答案 0 :(得分:6)

试试这样:

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

答案 1 :(得分:5)

IIRC Enumerable.Empty<T>是一个静态的只读枚举,用于将可枚举的空存储器无法传递给方法。它并不意味着用作空集合的“起点”。这可能是你收到错误的原因。

选择存储机制(例如List<T>)并将其用作支持字段的类型。然后你可以初始化它

  1. 在班级定义中,
  2. 在构造函数中,或
  3. 首先得到:
  4. 的示例:

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

答案 2 :(得分:1)

在自定义getter中,我通常会看到用于在返回时设置私有后备字段的空合并运算符(??):

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

您也可以尝试将默认值设置为空数组:

(_drivers = new Claimant[]{})

答案 3 :(得分: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();