I have getting an error during the model bind when I use try to bind a model with these two properties:
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;
}
}
Error:
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:
If I change the properties to basic auto properties:
public IEnumerable<Claimant> Drivers { get; set; }
public IEnumerable<Property> Vehicles { get; set; }
Everything works fine.
Why would the model binding have issues when the setters are the same as the auto property setters?
Edit - Reading through the default model binder source eventually leads you to this, where the first line is calling Clear()
against the property, so when I returned the Empty<T>
it obviously is not going to work.
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);
}
}
}
Try like this:
get
{
return _drivers ?? new List<Claimant>();
}
IIRC Enumerable.Empty<T>
is a static, read-only enumerable used to pass an empty storage-agnostic enumerable to methods. It is not meant to be used as a "starting point" for an empty collection. That is likely why you're getting the error.
Choose a storage mechanism (eg List<T>
) and use that as the type for your backing field. You can then either initialize it
examples:
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;
}
}
In a custom getter, I have typically seen the null coalescing operator (??) used to set the private backing field upon returning:
private IEnumerable<Claimant> _drivers;
public IEnumerable<Claimant> Drivers
{
get
{
return _drivers ?? (_drivers = Enumerable.Empty<Claimant>());
}
set
{
_drivers = value;
}
}
You could also try to set the default value to an empty array:
(_drivers = new Claimant[]{})
Find out the exact property name and type causing the problem:
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);
}
}
Set this binder as the default:
ModelBinders.Binders.DefaultBinder = new TestBinder();
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.