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,
public ActionResult Index() { return View(); } [HttpPost] public ActionResult Index([ModelBinder(typeof(TwoDimensionalArrayBinder<int>))] int[,] MatrixA, [ModelBinder(typeof(TwoDimensionalArrayBinder<int>))] int[,] MatrixB, [ModelBinder(typeof(TwoDimensionalArrayBinder<string>))] string[,] Comments) { int[,] Reslutant = new int[MatrixA.GetLength(0), MatrixB.GetLength(1)]; for (int i = 0; i < MatrixA.GetLength(0); i++) for (int j = 0; j < MatrixB.GetLength(1); j++) for (int k = 0; k < MatrixA.GetLength(1); k++) Reslutant[i, j] += MatrixA[i, k] * MatrixB[k, j]; return View("MatrixResult", new MatrixResult { MatrixA = MatrixA, MatrixB = MatrixB, Comments = Comments, Reslutant = Reslutant }); }
Next create a new class file TwoDimensionalArrayBinder.cs inside Model folder and add the following code,
public class TwoDimensionalArrayBinder<T> : IModelBinder { public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { string key = bindingContext.ModelName; try { int totalRows = 0; while (bindingContext.ValueProvider.GetValue(key + totalRows) != null) totalRows++; ValueProviderResult[] val = new ValueProviderResult[totalRows]; for (int i = 0; i < totalRows; i++) val[i] = bindingContext.ValueProvider.GetValue(key + i); if (val.Length > 0) { int totalColumn = ((string[])val[0].RawValue).Length; T[,] twoDimensionalArray = new T[totalRows, totalColumn]; StringBuilder attemptedValue = new StringBuilder(); for (int i = 0; i < totalRows; i++) { for (int j = 0; j < totalColumn; j++) { twoDimensionalArray[i, j] = (T)Convert.ChangeType(((string[])val[i].RawValue)[j], typeof(T)); attemptedValue.Append(twoDimensionalArray[i, j]); } } bindingContext.ModelState.SetModelValue(key, new ValueProviderResult(twoDimensionalArray, attemptedValue.ToString(), CultureInfo.InvariantCulture)); return twoDimensionalArray; } } catch { bindingContext.ModelState.AddModelError(key, "Data is not in correct Format"); } return null; } }
Next create a new class file MatrixResult.cs inside Model folder and add the following code,
public class MatrixResult { public int[,] MatrixA; public int[,] MatrixB; public int[,] Reslutant; public string[,] Comments; }
Next open the Index view of HomeController and add the following lines,
<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %> <asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server"> Home Page </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> <%using(Html.BeginForm()){ %> <table> <tr> <td> <table cellpadding="0px" cellspacing="0px" style="border:0px"> <caption>Matrix A</caption> <%for (int i = 0; i < 3; i++) {%> <tr> <% for (int j = 0; j < 3; j++) {%> <td style="padding:0px;border:0px"> <%=Html.TextBox("MatrixA" + i, null, new {style="width:40px",maxlength="4" })%> </td> <%}%> </tr> <%} %> </table> </td> <td style="font-size:20px;font-weight:bold;"> X </td> <td> <table cellpadding="0px" cellspacing="0px" style="border:0px"> <caption>Matrix B</caption> <%for (int i = 0; i < 3; i++) {%> <tr> <% for (int j = 0; j < 3; j++) {%> <td style="padding:0px;border:0px"> <%=Html.TextBox("MatrixB" + i, null, new {style="width:40px",maxlength="4" })%> </td> <%}%> </tr> <%} %> </table> </td> <td> <table cellpadding="0px" cellspacing="0px" style="border:0px"> <caption>Comments</caption> <%for (int i = 0; i < 3; i++) {%> <tr> <% for (int j = 0; j < 3; j++) {%> <td style="padding:0px;border:0px"> <%=Html.TextBox("Comments" + i, null, new {style="width:100px" })%> </td> <%}%> </tr> <%} %> </table> </td> </tr> <tr> <td align="center" colspan="4"> <input type="submit" /> </td> </tr> </table> <%} %> </asp:Content>
Next create a MatrixResult partial view and add the following lines,
<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<TwoDimensionalModelBinder.Models.MatrixResult>" %> <asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server"> Matrix Result </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> <table> <tr> <td> <table> <caption>Matrix A</caption> <%for (int i = 0; i < 3; i++) {%> <tr> <% for (int j = 0; j < 3; j++) {%> <td> <%=Model.MatrixA[i,j] %> </td> <%}%> </tr> <%} %> </table> </td> <td style="font-size:20px;font-weight:bold;"> X </td> <td> <table> <caption>Matrix B</caption> <%for (int i = 0; i < 3; i++) {%> <tr> <% for (int j = 0; j < 3; j++) {%> <td> <%=Model.MatrixB[i,j] %> </td> <%}%> </tr> <%} %> </table> </td> <td style="font-size:20px;font-weight:bold;"> = </td> <td> <table> <caption>Result</caption> <%for (int i = 0; i < 3; i++) {%> <tr> <% for (int j = 0; j < 3; j++) {%> <td> <%=Model.Reslutant[i,j] %> </td> <%}%> </tr> <%} %> </table> </td> <td> <table> <caption>Comments</caption> <%for (int i = 0; i < 3; i++) {%> <tr> <% for (int j = 0; j < 3; j++) {%> <td> <%=Model.Comments[i,j] %> </td> <%}%> </tr> <%} %> </table> </td> </tr> <tr> <td align="center" colspan="6"> <%=Html.ActionLink("Go To Home Page","Index") %> </td> </tr> </table> </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.