Model Binder For Two Dimensional Array
One of the feature that I really like most in ASP.NET MVC is Model Binding. ASP.NET MVC model binding makes it easy to bind action method parameters with incoming HTTP request data. By default DefaultModelBinder is responsible for binding posted values to action method parameters because DefaultModelBinder knows the rules of action method parameter binding. The default binder does excellent work for most but not all cases. For example, DefaultModelBinder will not bind two dimensional array parameter. For these cases, ASP.NET MVC allows you to create a custom model binder. In this article I will show you how to create a custom model binder that allows you to bind two dimensional array parameter.
Description:
For demonstrating how to create and use custom model binder for two dimensional array, let's say you are creating a web page that is collecting two matrix data and then displaying matrix multiplication result. Also only for demonstration, you are collecting an extra comment matrix. So the web page collecting matrix data will be look like,
After submitting these matrices you will see the following screen,
Let's create a sample ASP.NET MVC application that accomplishes this scenario. First of all open HomeController.cs and add the following code,
01 |
public
ActionResult Index()
|
02 |
{
|
03 |
return
View();
|
04 |
}
|
05 |
06 |
[HttpPost]
|
07 |
public
ActionResult Index([ModelBinder(typeof(TwoDimensionalArrayBinder<int>))] int[,] MatrixA, [ModelBinder(typeof(TwoDimensionalArrayBinder<int>))] int[,] MatrixB, [ModelBinder(typeof(TwoDimensionalArrayBinder<string>))] string[,] Comments)
|
08 |
{
|
09 |
int[,] Reslutant = new
int[MatrixA.GetLength(0),
MatrixB.GetLength(1)];
|
10 |
for
(int
i = 0; i < MatrixA.GetLength(0);
i++)
|
11 |
for
(int
j = 0; j < MatrixB.GetLength(1);
j++)
|
12 |
for
(int
k = 0; k < MatrixA.GetLength(1);
k++)
|
13 |
Reslutant[i, j] += MatrixA[i, k] * MatrixB[k,
j];
|
14 |
return
View("MatrixResult", new
MatrixResult { MatrixA = MatrixA, MatrixB =
MatrixB, Comments = Comments, Reslutant =
Reslutant });
|
15 |
}
|
Next create a new class file TwoDimensionalArrayBinder.cs inside Model folder and add the following code,
01 |
public
class
TwoDimensionalArrayBinder<T> :
IModelBinder
|
02 |
{
|
03 |
public
object
BindModel(ControllerContext
controllerContext, ModelBindingContext
bindingContext)
|
04 |
{
|
05 |
string
key = bindingContext.ModelName;
|
06 |
try
|
07 |
{
|
08 |
int
totalRows = 0;
|
09 |
while
(bindingContext.ValueProvider.GetValue(key +
totalRows) != null)
|
10 |
totalRows++;
|
11 |
ValueProviderResult[] val = new
ValueProviderResult[totalRows];
|
12 |
for
(int
i = 0; i < totalRows; i++)
|
13 |
val[i] =
bindingContext.ValueProvider.GetValue(key +
i);
|
14 |
if
(val.Length > 0)
|
15 |
{
|
16 |
int
totalColumn = ((string[])val[0].RawValue).Length;
|
17 |
T[,] twoDimensionalArray = new
T[totalRows, totalColumn];
|
18 |
StringBuilder attemptedValue = new
StringBuilder();
|
19 |
for
(int
i = 0; i < totalRows; i++)
|
20 |
{
|
21 |
for
(int
j = 0; j < totalColumn; j++)
|
22 |
{
|
23 |
twoDimensionalArray[i, j] =
(T)Convert.ChangeType(((string[])val[i].RawValue)[j], typeof(T));
|
24 |
attemptedValue.Append(twoDimensionalArray[i,
j]);
|
25 |
}
|
26 |
}
|
27 |
bindingContext.ModelState.SetModelValue(key, new
ValueProviderResult(twoDimensionalArray,
attemptedValue.ToString(),
CultureInfo.InvariantCulture));
|
28 |
return
twoDimensionalArray;
|
29 |
}
|
30 |
}
|
31 |
catch
|
32 |
{
|
33 |
bindingContext.ModelState.AddModelError(key, "Data is not in correct Format");
|
34 |
}
|
35 |
return
null;
|
36 |
}
|
37 |
}
|
Next create a new class file MatrixResult.cs inside Model folder and add the following code,
1 |
public
class
MatrixResult
|
2 |
{
|
3 |
public
int[,] MatrixA;
|
4 |
public
int[,] MatrixB;
|
5 |
public
int[,] Reslutant;
|
6 |
public
string[,] Comments;
|
7 |
}
|
Next open the Index view of HomeController and add the following lines,
01 |
<%@ Page Language="C#"
MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage" %>
|
02 |
03 |
<asp:Content
ID="Content1"
ContentPlaceHolderID="TitleContent"
runat="server">
|
04 |
Home Page
|
05 |
</asp:Content>
|
06 |
07 |
<asp:Content
ID="Content2"
ContentPlaceHolderID="MainContent"
runat="server">
|
08 |
<%using(Html.BeginForm()){ %>
|
09 |
<table>
|
10 |
<tr>
|
11 |
<td>
|
12 |
<table
cellpadding="0px"
cellspacing="0px"
style="border:0px">
|
13 |
<caption>Matrix A</caption>
|
14 |
<%for (int i = 0; i < 3; i++)
|
15 |
{%>
|
16 |
<tr>
|
17 |
<% for (int j = 0; j < 3; j++)
|
18 |
{%>
|
19 |
<td
style="padding:0px;border:0px">
|
20 |
<%=Html.TextBox("MatrixA" + i, null, new
{style="width:40px",maxlength="4"
})%>
|
21 |
</td>
|
22 |
<%}%>
|
23 |
</tr>
|
24 |
<%} %>
|
25 |
</table>
|
26 |
</td>
|
27 |
<td
style="font-size:20px;font-weight:bold;">
|
28 |
X
|
29 |
</td>
|
30 |
<td>
|
31 |
<table
cellpadding="0px"
cellspacing="0px"
style="border:0px">
|
32 |
<caption>Matrix B</caption>
|
33 |
<%for (int i = 0; i < 3; i++)
|
34 |
{%>
|
35 |
<tr>
|
36 |
<% for (int j = 0; j < 3; j++)
|
37 |
{%>
|
38 |
<td
style="padding:0px;border:0px">
|
39 |
<%=Html.TextBox("MatrixB" + i, null, new
{style="width:40px",maxlength="4"
})%>
|
40 |
</td>
|
41 |
<%}%>
|
42 |
</tr>
|
43 |
<%} %>
|
44 |
</table>
|
45 |
</td>
|
46 |
<td>
|
47 |
<table
cellpadding="0px"
cellspacing="0px"
style="border:0px">
|
48 |
<caption>Comments</caption>
|
49 |
<%for (int i = 0; i < 3; i++)
|
50 |
{%>
|
51 |
<tr>
|
52 |
<% for (int j = 0; j < 3; j++)
|
53 |
{%>
|
54 |
<td
style="padding:0px;border:0px">
|
55 |
<%=Html.TextBox("Comments" + i, null, new
{style="width:100px" })%>
|
56 |
</td>
|
57 |
<%}%>
|
58 |
</tr>
|
59 |
<%} %>
|
60 |
</table>
|
61 |
</td>
|
62 |
</tr>
|
63 |
<tr>
|
64 |
<td
align="center"
colspan="4">
|
65 |
<input
type="submit"
/>
|
66 |
</td>
|
67 |
</tr>
|
68 |
</table>
|
69 |
<%} %>
|
70 |
</asp:Content>
|
Next create a MatrixResult partial view and add the following lines,
01 |
<%@ Page Language="C#"
MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<TwoDimensionalModelBinder.Models.MatrixResult>" %>
|
02 |
03 |
<asp:Content
ID="Content1"
ContentPlaceHolderID="TitleContent"
runat="server">
|
04 |
Matrix Result
|
05 |
</asp:Content>
|
06 |
07 |
<asp:Content
ID="Content2"
ContentPlaceHolderID="MainContent"
runat="server">
|
08 |
<table>
|
09 |
<tr>
|
10 |
<td>
|
11 |
<table>
|
12 |
<caption>Matrix A</caption>
|
13 |
<%for (int i = 0; i < 3; i++)
|
14 |
{%>
|
15 |
<tr>
|
16 |
<% for (int j = 0; j < 3; j++)
|
17 |
{%>
|
18 |
<td>
|
19 |
<%=Model.MatrixA[i,j] %>
|
20 |
</td>
|
21 |
<%}%>
|
22 |
</tr>
|
23 |
<%} %>
|
24 |
</table>
|
25 |
</td>
|
26 |
<td
style="font-size:20px;font-weight:bold;">
|
27 |
X
|
28 |
</td>
|
29 |
<td>
|
30 |
<table>
|
31 |
<caption>Matrix B</caption>
|
32 |
<%for (int i = 0; i < 3; i++)
|
33 |
{%>
|
34 |
<tr>
|
35 |
<% for (int j = 0; j < 3; j++)
|
36 |
{%>
|
37 |
<td>
|
38 |
<%=Model.MatrixB[i,j] %>
|
39 |
</td>
|
40 |
<%}%>
|
41 |
</tr>
|
42 |
<%} %>
|
43 |
</table>
|
44 |
</td>
|
45 |
<td
style="font-size:20px;font-weight:bold;">
|
46 |
=
|
47 |
</td>
|
48 |
<td>
|
49 |
<table>
|
50 |
<caption>Result</caption>
|
51 |
<%for (int i = 0; i < 3; i++)
|
52 |
{%>
|
53 |
<tr>
|
54 |
<% for (int j = 0; j < 3; j++)
|
55 |
{%>
|
56 |
<td>
|
57 |
<%=Model.Reslutant[i,j] %>
|
58 |
</td>
|
59 |
<%}%>
|
60 |
</tr>
|
61 |
<%} %>
|
62 |
</table>
|
63 |
</td>
|
64 |
<td>
|
65 |
<table>
|
66 |
<caption>Comments</caption>
|
67 |
<%for (int i = 0; i < 3; i++)
|
68 |
{%>
|
69 |
<tr>
|
70 |
<% for (int j = 0; j < 3; j++)
|
71 |
{%>
|
72 |
<td>
|
73 |
<%=Model.Comments[i,j] %>
|
74 |
</td>
|
75 |
<%}%>
|
76 |
</tr>
|
77 |
<%} %>
|
78 |
</table>
|
79 |
</td>
|
80 |
</tr>
|
81 |
<tr>
|
82 |
<td
align="center"
colspan="6">
|
83 |
<%=Html.ActionLink("Go To Home
Page","Index") %>
|
84 |
</td>
|
85 |
</tr>
|
86 |
</table>
|
87 |
</asp:Content>
|
I am taking the convention over configuration approach. If MatrixA is a two dimensional array then MatrixA0, MatrixA0, MatrixA0,....., denotes the first Row of MatrixA and MatrixA1, MatrixA1, MatrixA1,....., denotes the second Row of MatrixA and so on. If you have following names in your view, MatrixA0, MatrixA0, MatrixA1, MatrixA1, then you can say that you have a two dimensional array with two rows and two columns.
TwoDimensionalArrayBinder simply find the total number of rows and columns first, then create a two dimensional array of this size and then fill this new array with RawValue.
Index view display the 3 matrices. Two matrices are used for matrix multiplication and one is used to display comments. HomeController's Post Index action will receive these values as two dimensional arrays after user submit the form values and TwoDimensionalArrayBinder binds these submitted values as two dimensional arrays. Inside this action, I am using the model logic(multiplication of two matrix) inside controller only for demonstration. Finally MatrixResult view with MatrixResult model is returned. MatrixResult view simply display the result of these matrix multiplication(see the above diagram).
Summary:
In
this article I showed how to create a custom model binder
for two dimensional array. I also showed an example of using
this new custom model binder in your action method. This
article also aims to help you to create custom model binder
in ASP.NET MVC. I hope this will help you to create your own
custom model binder and hope you will enjoy this article
too.