I have revised my answer. There were a few problems that I ran into when trying to get this working. Detailed below is the problem and the solution I used.
The json: The json you provided did not match the Model you provided. So I assumed the json string should have included something like this:
`DataEntrada: "1/1/2014"`
The model: Your model describes only EntityName. The deserialized json is a list. These are two different things. So I modified the json to be an object that defines EntityNames (list of EntityName), like this:
`data = { EntityNames: [{ DataEntrada: "1/1/2014" }] };`
and then I implemented this class..this will be the result of deserialization:
public class EntityInfo
{
public EntityName[] EntityNames { get; set; }
}
and finally, modified the ActionMethod like so:
public JsonResult SaveActionName([ModelBinder(typeof(JsonArrayValidationModelBinder<EntityInfo>))]EntityInfo viewModel)
Validation: Validating EntityNames was not as easy to implement as I thought it would be. I could not get the validation attribute for EntityName to fire during model binding (being a member of a list). So, I implemented a custom validator derived from 'ValidationAttribute' like this:
public class EntityNamesValidation : ValidationAttribute
{
public override bool IsValid(object value)
{
EntityName[] list = (EntityName[])value;
foreach (EntityName e in list)
{
if (string.IsNullOrEmpty(e.DataEntrada.ToString()))
return false;
// more checks performed here
}
return true;
}
}
and then I applied EntityNamesValidation attribute to EntityNames and EntityInfo, like so:
[EntityNamesValidation]
public EntityName[] EntityNames { get; set; }
Incorrect model during bind: The JsonArrayValidationModelBinder was using a bindingContext that did not have an instance of anything. If you debug BindModel before base.BindModel you will see that bindingContext.Model is null. So what I did was set bindingContext.ModelMetadata.Model = model after deserialization and before the call to base.BindModel. I also moved base.BindModel in the code to fire just before model is returned...see below
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
[...]
bindingContext.ModelMetadata.Model = model;
base.BindModel(controllerContext, bindingContext);
return model;
}
Verification: I did not unit test this, but I did place a breakpoint in the ActionMethod. I then used the following json:
data = { EntityNames: [{ DataEntrada: "1/1/2014" }, { DataEntrada: null }] };
when the code reached the breakpoint, ModelState.IsValid is false. I then changed json to this:
data = { EntityNames: [{ DataEntrada: "1/1/2014" }, { DataEntrada: "2/19/2014" }] };
when the code reached the breakpoint, ModelState.IsValid is true.
This approach works, but is not ideal. I think you want validation to occur without creating custom code and use MVC to handle this.
I hope this gets you a step further.
ALL THE CODE
javascript
data = { EntityNames: [{ DataEntrada: "1/1/2014" }, { DataEntrada: null }] };
var jsonOfLog = JSON.stringify(data);
$.ajax({
type: 'POST',
dataType: 'text',
url: "/EntityData/SaveActionName",
data: jsonOfLog,
success: function (data) {
alert(data);
},
error: function (result) {
alert(result);
}
,
async: false
});
models
public class EntityInfo
{
[EntityNamesValidation]
public EntityName[] EntityNames { get; set; }
}
public class EntityName
{
[Display(Name = "Data Entrada")]
[DataType(DataType.Date)]
[Required]
public DateTime? DataEntrada { get; set; }
}
custom validator
public class EntityNamesValidation : ValidationAttribute
{
public override bool IsValid(object value)
{
EntityName[] list = (EntityName[])value;
foreach (EntityName e in list)
{
if (string.IsNullOrEmpty(e.DataEntrada.ToString()))
return false;
// more checks performed here
}
return true;
}
}
BindModel
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var contentType = controllerContext.HttpContext.Request.ContentType;
String bodyText;
Stream stream = null;
try
{
stream = controllerContext.HttpContext.Request.InputStream;
stream.Seek(0, SeekOrigin.Begin);
using (var reader = new StreamReader(stream))
{
stream = null;
bodyText = reader.ReadToEnd();
}
}
finally
{
if (stream != null)
stream.Dispose();
}
if (string.IsNullOrEmpty(bodyText))
{
return null;
}
var model = new JavaScriptSerializer().Deserialize<T>(bodyText);
bindingContext.ModelMetadata.Model = model;
base.BindModel(controllerContext, bindingContext);
return model;
}
ActionMethod
[HttpPost]
public JsonResult SaveActionName([ModelBinder(typeof(JsonArrayValidationModelBinder<EntityInfo>))]EntityInfo viewModel)