ASP.NET MVC: Post a list or collection of complex types with non-sequential indices

In ASP.Net MVC when you post your form then DefaultModelBinder bind your form’s submitted value. Now what if your model is list / collection of complex type which you want to post and the case is become even worse when the sequence or let say index is not maintained. Okay let’s see it in detail.

In one of my project developed in MVC 4, I had a requirement to bind list of complex type model to view. Furthermore, from this view user can add or remove items using Jquery / JavaScript before submitting it. Let’s create this situation. Let say, I want to add more than one students in “ClassRoom” table in single submit. So, I have model named – “ClassRoomViewModel” which contain property “Students” of type List.

public class ClassRoomViewModel
{
	public List<Student> Students { get; set; }
}

public class Student
{
	public string StudentName { get; set; }
	public int Age { get; set; }
}

As this is student entry form. So it opens as an empty form using below Action method that renders a view.

public ActionResult Index()
{
	var classRoom = new ClassRoomViewModel()
	{
		Students = new List<Student>()
	};
	
	return View(classRoom);
}

Now let’s create a view which call another partial view. Here is the code snippet.

@model PostListWithBrokenSequence.Models.ClassRoomViewModel

@{
	ViewBag.Title = "Home Page";
}
<div class="jumbotron">
<h1>Enroll Students</h1>
</div>
<div class="row">
	@using (Html.BeginForm())
	{
		<div class="col-md-4">
			<h2>Add Student</h2>
			@Html.Partial("_PartialAddStudent", 0)

			@Html.ActionLink("Add Student", "LoadBlankFormView", null, new {@id = "lnkAddStudent", @class = "btn btn-primary"})
			<button class="btn btn-default" type="submit"> Save </button>
		</div>
	}
</div>

And for partial view named – “_PartialAddStudent.cshtml”. here is code snippet.

@model int

<div class="form-horizontal" id="dv_@Model" data-rownum="@Model">
    <!-- <input type="hidden" name="Students.Index" value="@Model" /> -->
    <div class="form-group">
        @Html.Label("Name", new { @class = "control-label" })
        <div>
            @Html.TextBox("Students[" + Model + "].StudentName", "", new { @class = "form-control" })
        </div>
    </div>

    <div class="form-group">
        @Html.Label("Age", new {@class = "control-label"})
        <div>
            @Html.TextBox("Students[" + Model + "].Age", "", new {@class = "form-control"})
        </div>
    </div>
    <div class="form-group">
        <a href="#" class="deleteStudent btn btn-primary">Remove</a>
    </div>
    <hr/>
</div>

When you run the application it looks like this in browser.

AddStudentForm

To bind complex objects like list etc., we need to provide an index for each row as shown in “_PartialAddStudent” view. Yes, Index which start with 0 and incremented by 1. That’s why I render “_PartialAddStudent” view with 0 index in “Index” view using HTML.Partial() method. As you see in the above code that I have manually build names and ids of all control in the partialview. Well you can use templated helper by creating strongly typed partialview in “~/Views/Shared/ EditorTemplates” and achieve the same result with less code by using the built-in Html.EditorFor() or Html.EditorForModel() helpers, but in our case it would be difficult to use as we add and remove rows dynamically from the DOM.

All-right coming to the point, Index must be in sequence when posting the form with list of complex object, otherwise MVC DefaultModelBinder can’t able to bind it to our complex object (In our case its List<Student>). Okay let’s see it practically. As you see in above image, “Add Student” button which dynamically add our partialview (row) to DOM using jquery. Below is the code snippet.

$('#lnkAddStudent').click(function() {
	var index = parseInt($('.form-horizontal:last').data('rownum')) + 1;

	$.ajax({
		url: this.href + '/' + index,
		type: "GET",
		cache: false,
		success: function(html) {
			$('.form-horizontal:last').after(html);
		}
	});
	return false;
});

When you click on “Add button”, it makes AJAX request and call “LoadBlankFormView” action method which append partialviews’ html to DOM.

public ActionResult LoadBlankFormView(int id)
{
	return PartialView("_PartialAddStudent", id);
}

Now run the application and click on “Add Student” button 2 times and then fill the form and submit the form by click on “Save” button.

StudentFormWithData

As there is no broken index so DefaultModelBinder binds our complex object correctly when you debug the application, you can see all records posted back correctly as show in below image.

FormPostWithProperIndex

Now let’s remove any of the row which break the index and makes the index as 0, 2, ..etc and our HTML in DOM looks like this –

<form action="/" method="post">        
<div class="col-md-4">
    <h2>Add Student</h2>
	<div>
		<div class="form-group">
			Name
			<div class="col-md-10">
				<input class="form-control" name="Students[0].StudentName" type="text" value="John Miller" />
			</div>
		</div>
		<div class="form-group">
			Age
			<div class="col-md-10">
				<input class="form-control" name="Students[0].Age" type="text" value="65" />
			</div>
		</div>
		<hr />
	</div>
	<div>
		<div class="form-group">
			Name
			<div class="col-md-10">
				<input class="form-control" name="Students[2].StudentName" type="text" value="Krishn" />
			</div>
		</div>
		<div class="form-group">
			Age
			<div class="col-md-10">
				<input class="form-control" name="Students[2].Age" type="text" value="90" />
			</div>
		</div>
	</div>
</div>
</form>

See the index in above code. Here is the “Remove” button code which removes row from the DOM.

$(document.body).on("click", "a.deleteStudent", function() {
	$(this).parents('div.form-horizontal:first').remove();
	return false;
});

Now when you post the above form with broken indices, you’ll lost the data from where the sequence get break. See the below image.

FormPostWithBreakIndex

Don’t worry, to overcome this problem we just need an extra hidden input field with the name – “list/collectionName.Index” for each item we need to bind to the list. In our case hidden input field name should be “Students.Index”.

<input name="Students.Index" type="hidden" value="@Model" />

The name of each of these hidden inputs must be the same with unique values. So after adding it to our “_PratialAddStudent” view, run application again and add 2 new records, remove any middle one and all. After doing all of these test cases at the end, look at rendered html code with broken sequence now looks like this –

ViewSourceOfBrokenIndices

Now click on “Save” button and post that form. Even with broken sequence DefaultModelBinder bind your form’s value correctly!!. You can download the sample demo project here. If you have any query then mail me or leave your comments.

That’s it guys for now. Hope you enjoy this post. Thank you.

Wcf parameter validation using IParameterInspector in WCF REST Service

In this blog post we’ll see about a very important feature of the WCF and its extensibility and how it help us to control the behavior of the service. For one of our project, we have developed WCF REST Api to use it in iPhone App. One day we found one bug that one of the wcf service method was not returning the expected output because one input parameter value passed as blank. So we needed to validate parameter before service executed. After some research i have found this IParameterInspector interface which resolved my problem.

Let’s try to understand this powerful feature of wcf. IParameterInspector interface resides in System.ServiceModel.Dispatcher namespace which allows us to inspecting parameters passed in each operation before the call / service is executed and after the response is returned.

This interface contains only two methods:

public interface IParameterInspector
{
	void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState);
	object BeforeCall(string operationName, object[] inputs);
}

AfterCall (string, object[], object, object) :-  Called after client calls are returned and before service responses are sent.

BeforeCall (String, Object[]) :- This method invoked after parameters are deserialized but before they are dispatched to the service operation.

Okay we don’t go too much in theory. So let’s implement the IParameterInspector interface to create a custom parameter inspector which can view and validate the parameters before the call or after the call and see how it throws fault exception if any parameter value not validated.

Here i assume that WCF Service project is already created and let’s create our custom class which implements IParameterInspector interface. Well you can also create separate class library to create custom inspector class and then add this library reference to wcf project.

public class ValidateParameterInspectorAttribute : Attribute, IParameterInspector, IOperationBehavior
{

	public object BeforeCall(string operationName, object[] inputs)
	{
		InputParams.StartValidatingParameters(operationName, inputs);
		return null;
	}

	public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
	{
	}
	
	public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
	{
	}

	public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
	{
		dispatchOperation.ParameterInspectors.Add(this);
	}
}

To attach parameter inspector as an attribute to service operation, i have derived it from “Attribute” class and to add it in service i have implemented “IOperationBehavior” interface. Using “ApplyDispatchBehavior()” method you can add parameter inspector in the DispatchOperation object as shown in above code. Below code snippet shows how you can attach the attribute to an operation.

[ServiceContract]
public interface IStudentService
{
	
	[ValidateParameterInspector]
	[OperationContract]
	[WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json,
			UriTemplate = "/SaveStudentRecord")]
	Response SaveStudentRecord();
	
}

That’s it. No need to do change in web.config file using above approach.

Now when i call SaveStudentRecord() service method from the iPhone app or from the “Advanced Rest Client” then parameter inspector first invoke “BeforeCall()” method, which further call another method of “InputParams” class to validate input parameter as show in below code snippet.

public class InputParams
{
	internal static void StartValidatingParameters(string operationName, object[] inputs)
	{
		var methodParam = inputs.FirstOrDefault() as StudentViewModel;

		if (methodParam != null &&
			string.IsNullOrEmpty(methodParam.StudentName))
		{
			LogErrorMessage(ThrowRequiredValueException(operationName, "StudentName"));
			ThrowFaultException();
		}
		else if (string.IsNullOrEmpty(methodParam.BirthDate))
		{
			LogErrorMessage(ThrowRequiredValueException(operationName, "BirthDate"));
			ThrowFaultException();
		}
		else if (methodParam.RoleID <= 0)
		{
			LogErrorMessage(ThrowRangeException(operationName, "RoleID"));
			ThrowFaultException();
		}
		else if (methodParam.Standard <= 0)
		{
			LogErrorMessage(ThrowRangeException(operationName, "Standard"));
			ThrowFaultException();
		}
		
	}
	
	private static void ThrowFaultException()
	{
		WebOperationContext.Current.OutgoingResponse.ContentType = "application/json";
		var wfc = new WebFaultException<Response>(new Response
			(
				false,
				"Oops, Invalid parameter found!"
			), System.Net.HttpStatusCode.OK);
		throw wfc;
	
	}
	
	private static Exception ThrowRangeException(string methodName, string parameterName)
	{
		return new ArgumentException(string.Format("API Name: {0} - Parameter cannot be less than zero.", methodName), parameterName);
	}

	private static Exception ThrowSameValueException(string methodName, string parameter1, string parameter2)
	{
		return new ArgumentException(string.Format("API Name: {0} - Parameter {1} value must not be the same as {2}.", methodName, parameter1, parameter2));
	}

	private static Exception ThrowRequiredValueException(string methodName, string parameterName)
	{
		return new ArgumentNullException(parameterName, string.Format("API Name: {0} - Parameter cannot be blank or null.", methodName));
	}

}

As you see in above code that actual error message would logged for developers’ use and at client side it display only general error message. Well, you can change in above code as per your application architecture and requirement. For demo purpose i have show you only blank value validation and range validation. One important thing is – this is not production-ready code.

And that’s it for now. Hope you enjoyed this post. Leave your comments.

Introduction to new CONCAT() function in SQL Server 2012

SQL Server 2012 comes with some new string functions and one of them is CONCAT. In one of my project, I had a requirement to combine some columns and display it as single column in the application. Very simple requirement right, But wait why I used CONCAT function? I can use “+” operator to concat columns!! We’ll see it in detail in this blog post. Well, this blog post is also inspired by one of the [question] asked in Stackoverflow.com.

All right, CONCAT function helps to combine two or more column’s value into a single string value. The basic syntax is –

CONCAT (string_value1, string_value2 [, string_valueN ])

And below script shows the use of the function –

SELECT FirstName ,
LastName ,
Age ,
CONCAT(FirstName + ' ', LastName + ' ', Age, ' Years Old')
FROM dbo.Student;

Concat

CONCAT function takes n number of parameters and combines them into single string. But it requires a minimum of two input values otherwise it throws an error as shown below.

Concat_With_Error

Though, you can use both “+” operator and CONCAT Function in sql server 2012 and above, but then what is the difference between the two ? Here we go by example.

⇒ Handling of NULL:

When you use NULL or any column value is null then “+” operator will make whole result NULL. On the other hand CONCAT function treats NULL as an empty string. Let’s see in below script.

SELECT 'Hello' + NULL + ' World!' AS WithNULL;

SELECT CONCAT ('Hello', NULL, ' World!') AS WithConcat;

SELECT FirstName ,
LastName ,
Age ,
CONCAT(FirstName + ' ', LastName + ' ', Age, ' Years Old')
FROM dbo.Student;

Concat_With_NULL

⇒ Handling of Datatype Conversion:

What if you want to concatenate more than one column of different datatype. For exa. You want to concat INT and String value together. See the below script. When you run it using “+” operator, it throws an error as shown below.

SELECT Firstname + ' is '
+ Age + ' years old' FROM dbo.Student;

Using_Plus_Operator

Now, to resolve that error we need to do explicit conversion as shown in below script.

SELECT Firstname + ' is ' +
CONVERT(VARCHAR, Age) + ' years old' AS ExplicitConversion
FROM dbo.Student;

Explicit_Conversion

Now let’s do the same thing using CONCAT function –

SELECT CONCAT (Firstname, ' is ', Age, ' years old') AS UsingConcat
FROM dbo.Student;  

Concat_Implicit_Conversion

No error!! Well, CONCAT() Function does an implicit conversion to string datatype for all arguments passed in the function. One more thing, in SQL Server 2012 and above you can use both CONCAT() Function and “+” operator. But the CONCAT() Function is recommended. Why? Because its syntax is easy to follow and your code looks neat and cleaner.

That’s it for now. Hope you enjoyed this post.

Getting started with analytic functions in SQL Server 2012 Part-1

SQL Server 2012 comes with a lot of new feature. Window analytic function is one of them. Well as usual in my free time I generally active on stackoverflow.com. Well, a few days ago there was one question asked by one of the user in SO. I’m not going to explain this question here. Again on the same day another question was asked which was related to one of the feature that i covered in this post. After answering both the question, I thought why should not write the blog post for these analytic functions introduced in SQL server 2012.

Why Analytic functions

Analytic functions help you to get other information like access immediate next record, or next-to-next record or previous records etc. from the results without any self-joins, derived tables and subqueries and all. For example, let’s started with library management example. As a librarian in my college library, if I want to know that – That particular book was given to which student for the first time to read,  then who was the second student to read that book and who is the 3rd student and who is the last and so on. Well you can get all of these information in single SQL statement without any self join and all the stuff. Analytic functions really ease the developer’s life. We’ll see it one-by-one.

SQL Server 2012, comes with 8 analytic functions and its — PERCENT_RANK, CUME_DIST, PERCENTILE_CONT, PERCENTILE_DISC, LEAD, LAG, FIRST_VALUE and LAST_VALUE. In this first part we’ll understand LEAD, LAG, FIRST_VALUE and LAST_VALUE functions. Okay guys let’s start our journey.

FIRST_VALUE / LAST_VALUE:

First_Value() returns the first value of the result or partition. Last_Value() function does the opposite, it returns the last value of the result or partition. Below is the skeleton or syntax of it.

FIRST_VALUE / LAST_VALUE (Column name)
    OVER ( (optional)partition by   order by   (optional)rows / range clause )

Now, Let’s understand each part.

COLUMN NAME:  which column value you want to return.

PARTITION BY:  Divide the results into partition by specified criteria. This is optional.

ROWS / RANGE Clause:  This clause limits the partition result by specifying the start and end point. For that new keyword introduced in sql server like – UNBOUNDED PRECEDING and UNBOUNDED FOLLOWING and CURRENT ROW.

Before we go ahead, let’s take a small overview of each keyword because it’s also equally important to understand and how analytic functions use these keywords to limit the result.

  • UNBOUNDED PRECEDING: The range start at the first row of the partition. i.e. All previous rows within the result or partition.
  • UNBOUNDED FOLLOWING: The range ends at the last row of the partition. i.e. All subsequent rows within the result or partition.
  • CURRENT ROW: The range begins at the current row or ends at the current row.
  • n PRECEDING or n FOLLOWING: The range starts or ends n rows before or after the current row.

Okay, now let’s play with FIRST_VALUE() function. Below query gives you the first value from the result set.

SELECT DISTINCT Books.BookID,
BookName ,
FIRST_VALUE(st.StudentID) OVER(PARTITION BY st.BookID ORDER BY st. StudentID) AS FirstReader
FROM [BookAssignedToStudent] st
INNER JOIN dbo.Books ON Books.BookID = st.BookID
ORDER BY BookID;

First_Value

And below is the query for Last_Value() function.

SELECT DISTINCT Books.BookID,
BookName ,
LAST_VALUE (st.StudentID) OVER(PARTITION BY st.BookID ORDER BY st. StudentID) AS LastReader
FROM [BookAssignedToStudent] st
INNER JOIN dbo.Books ON Books.BookID = st.BookID
ORDER BY BookID; 

Last_Value

Now let’s try to limit the result by setting start and end point like below –

So first we’ll use – “ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING” like this –

SELECT DISTINCT Books.BookID,
BookName ,
FIRST_VALUE(st.StudentID) OVER(PARTITION BY st.BookID ORDER BY st.StudentID ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS CurRow_UnbFoll
FROM [BookAssignedToStudent] st
INNER JOIN dbo.Books ON Books.BookID = st.BookID
ORDER BY BookID; 

First_Value with Current row and Unbounded Following.

Now let’s use – “ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW” like this –

SELECT DISTINCT Books.BookID,
BookName ,
FIRST_VALUE(st.StudentID) OVER(PARTITION BY st.BookID ORDER BY st.StudentID ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS UnbPrec_CurRow
FROM [BookAssignedToStudent] st
INNER JOIN dbo.Books ON Books.BookID = st.BookID
ORDER BY BookID; 

In the below image, above query’s result is at left side. I tried to compare left hand side result with the original records (right side) just for the sake of explanation.

First_Value with UnbPrece. and Current Row

Now let’s use – “ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW” with LAST_VALUE() function like this –

SELECT DISTINCT Books.BookID,
BookName ,
LAST_VALUE (st.StudentID) OVER(PARTITION BY st.BookID ORDER BY st.StudentID ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS UnbPrec_CurRow
FROM [BookAssignedToStudent] st
INNER JOIN dbo.Books ON Books.BookID = st.BookID
ORDER BY BookID; 

Query result of Last_Value with UnbPrec. and CurRow.

Well, you can do all these experiment in [ sql fiddle ] I have created for you. Just open [ this link ] and do the experiment with “RANGE” keyword also.

LAG / LEAD

The LEAD function is used to read a value from the next row / subsequent row and The LAG function is used to read a value from the previous row of the result or partition. If there is no next / previous row exists, then this function return NULL. Other thing is LAG and LEAD functions allow you to specify the offset it means how many rows you want to look forward or backward. It also supports default value if null is returned.  In our case let say if you want to know that who is the second or third student to whom the particular book was assigned then these functions really helpful for us.

Below is the skeleton or syntax of it –

LEAD / LAG (Column name,  (optional) Offset,  (opt.) Default Val.)
    OVER ( (optional)partition by   order by …)

Okay now let’s play with it.

SELECT DISTINCT Books.BookID,
BookName,
AssignDate,
st.StudentID,
LEAD(st.StudentID) OVER(PARTITION BY st.BookID ORDER BY st.StudentID) AS NextStudentID,
LEAD(st.StudentID, 2, '-1') OVER(PARTITION BY st.BookID ORDER BY st.StudentID) AS NextToNextStudentIDWithDefaultValue,
LAG(st.StudentID) OVER(PARTITION BY st.BookID ORDER BY st.StudentID) AS PreviousStudentID,
LAG(st.StudentID, 2, '-1') OVER(PARTITION BY st.BookID ORDER BY st.StudentID) AS PreviousToPreviousStudentIDWithDefaultValue
FROM [BookAssignedToStudent] st
INNER JOIN dbo.Books ON Books.BookID = st.BookID
ORDER BY BookID; 

Just for the sake of explanation, I have divided the above query result into two parts in the below image.

Lead

and for LAG function –

Lag

 

That’s it for now. Hope you enjoyed this post. Leave your comments regarding this post. Thank you.