Tuesday, November 8, 2011

ASP.Net MVC Submit Collection Of Models

If you are using ASP.Net MVC for your presentation layer there are situations where you want to submit collection of view models. As an example lets say you have view models like this,

Person

  • Id : int
  • Name : string
  • Addresses : Ilist

Address

  • Id : Int
  • Line1 : string
  • City : string
  • Type : string
The Person view model has a collection of addresses. 

Then you need to have an edit view for the person to edit all the details of the person view model. In the edit view there will be code like this to display current addresses to the user to edit/ add new or delete .

@model YourNameSpace.Models.Person
@using (Html.BeginForm("Edit", "Person", FormMethod.Post, new { enctype = "multipart/form-data", id = "personEditForm" }))
{
///.................
               @foreach (var address in Model.Addresses)
                   {
                        @Html.HiddenFor(m,address.Id);
                        @Html.EditorForl(m, address.Line1);
                           //............
                   }
//....................................
}
This will generate HTML for the address Id  and address Line1 like this,

…..


…..


……



Because there are several inputs with same name we can't submit all values in the form For that the names should be like "Addresses[0].Line1" as below.
…..


…..


……



Of course you can achieve this by creating an editor template for address. But you will be in trouble when you delete some address in the middle of the sequence. And also if you have more properties like address for the person view model it will abuse your template folder.
This is the solution which I created with help of jQuery. I have written a jQuery function to reset all the names of those collection properties . To get the collection name I have added a wrapper div for each address . Before reset the names the HTML is like this,
…..
…..
……

And then I added this jQuery to onClick event to reset names before submit,
$("#submitButton").click(fuction(){
 $('.collection').each(function (index, domEle) {
           var property=$(this).attr('propertyName');
             $(this).children('div').each(function (index, domEle) {
                 $('input,select',$(this)).each(function (){
                    var oldName;
                
                    var name= $(this).attr('name')+'';
                    var i=  name.indexOf(']');
                    if(i>0){
                         oldName= name.substr(i+2);
                     }else {
                         oldName=  $(this).attr('name');
                         
                     }
                     $(this).attr('name',property+'['+index+'].'+  oldName);
                     $(this).next('span').attr('name',property+'['+index+'].'+  oldName);// need to change the names of validations messages too.
                     $(this).next('span').attr('data-valmsg-for',property+'['+index+'].'+  oldName);
                });
             });
     });   
});



After reeting the names the HTML will be like this,
…..
…..
……

Sunday, October 23, 2011

ASP.Net MVC Client Validation For Dynamically Added Form

This is how I have implemented the client side validation with MVC3 client side validation with JQuery validation plugin. The requirement was to create a model dialog with a form which is loaded via an Ajax request to the server.

  1. Enable client validation in web.config
We need to enable client side validation in web.config application settings section like thism


 
 <appSettings>
          <add key="ClientValidationEnabled" value="true"/>
        <add key="UnobtrusiveJavaScriptEnabled" value="true"/>
 </appSettings>
    


    2 The form
 In your form you need to have a FormContext otherwise it will not generate validation attributes.  If the FormContext is null we need to create a new FormContext  like this in your view.
 
@{using (Html.BeginForm("Create", "Person", FormMethod.Post)
  {
      if (this.ViewContext.FormContext == null)
      {
          this.ViewContext.FormContext = new FormContext();
      }
    // .....
  }


    3 Phrase validation attributes 
 Then after the ajax request we need to phrase validation attributes using JQuery like this,<br />
 
$.get("@Url.Action("Create", "Person")", null, function (data) {
         $('#yourDivId').html(data);
         $("form").removeData("validator");
         $("form").removeData("unobtrusiveValidation");
        $.validator.unobtrusive.parse("form");          
   });

Saturday, October 22, 2011

Creating Categorized/Grouped Autocomplete Menu with JQuery

JQuery has given you a nice extendable library to create auto complete. But what if you want to customize it to have something like Facebook search autocomplete menu.  Here I have gathered several features of JQuery to make this customization. Here is my JQuery with ASP.net MVC3 but you can use whatever the backend which you can return a array of Json objects like this,
Here is my Search action in SearchController








And here is my JQuery code,



Monday, September 19, 2011

Convert MSTest code covarage results in to XML And view through Jenkins

If you are using Jenkins as your CI for a Net project it is not easy to publish code coverage results.In Jenkins without tools such as NCover wich is costly. Instead of this you can do this your own way.
First you need to convert MSTest results in to XML and then to HTML using xstl to publish it as an HTML report in Jenkins.
Step 1

GO to your test local.settings file in visual studio and set it to display the code coverage results an add your required targeted dll's. This is same as configuring visual studio to show the test results and code coverage.
Make sure you have added the test settings file to your version controlling system then it will be in the working space of the jenkins.
Then set path to the MSTest.exe
it may be in your \Microsoft Visual Studio 10.0\Common7\IDE folder


Step 2
Add a windows batch command to run MSTest (after running ms build) and to generate test results file (“reults.trx”) and Coverage report (“data.coverage”)

del results.trx
mstest /testcontainer:Example\TestProject1\bin\debug\TestProject1.dll /resultsfile:results.trx /testsettings:Example\local.testsettings


This will generate a code coverage result in binary format (data.coverage)

Step 3
write a console app to convert binary data.coverage file in to a XML and then it to HTML by xslt and run it after adding windows batch command in jenkins. Here is the example code for console app. Make sure you have add reference to Microsoft.VisualStudio.Coverage.Analysis.dll which you can find in the \Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies folder. And copy Microsoft.VisualStudio.Coverage.Symbols.dll to your bin directory which is also in same folder.



And here is the code of the style.xslt file


Thursday, July 14, 2011

Entity-Framework Differed loading (Lazy Loading) and immediate (Eager ) loading

You can use entity framework with lazy loading enabled to do everything . But when you try to do the performance testing you will find some crazy things going on inside. You can use SQL Server Profiler or a tool such as Entity Framework Profiler to see whats happening inside. By looking at SQL queries exacuting to ther server you can understand where to use Lazy loading or Immediate loading.
1.Lazy loading
Related entities are automatically loaded from the data source when you access a navigation property. With this type of loading, be aware that each navigation property that you access results in a separate query executing against the data source if the entity is not already in the ObjectContext. Here is a lazy loading enabled example
db.Configuration.LazyLoadingEnabled = true;// enable lazy loading
var student = db.Students.FirstOrDefault(st => st.PersonID == 1);// first queri will execute here
int i=student.Enrollments.Count;// Second query will execute here to load selected students enrolments

It will execute two separate queries to the server. First is to get student's sealer properties and second query is to load his enrollments.

2.Immediate/Eager Loading
In this type only one single request will go to the database. It will return all entities defined by the path in a single result set. You can specify the related data that you want to load with the query by specifying paths.
db.Configuration.LazyLoadingEnabled = false;
var student = db.Students.Include("Enrollments").FirstOrDefault(st => st.PersonID == 1);
int i=student.Enrollments.Count;

this will execute one query to load student's scaler properties and his navigational property(Collection of Enrollments).

How to choose the best method
You need to consider three things,

  1. How many connections that you are going to make with the database. If you are using lazy loading there will be a database call for all the reference points of a navigation properties if referred navigation property is not in the context.
  2. How much data that you are going to retrieve from databaseIf you choose to load all the data in initial query with differed loading it will be too slow when you have huge amount of data to retrieve.
  3. Complexity of the query . When you are using lazy loading the queries will be simple because all the data is not loaded in the initial query. If you use immediate loading it will make quires will be more complex with query paths

Saturday, July 9, 2011

Jquery Post URL problems in IIS hosted Eenvironments

If you have used JQuery post with ASP.Net MVC the URL in the post will be something like
"YourControllerName/ActionName"
Example:-
$.post("Home/CreatePerson", { name: "John", time: "2pm" },
    function(data) {
    alert(data);
});
This will work great until you deploy your project in IIS with an alias to your web application. Let say the give alias is "MyWeb". The the URL will be -your-iis-server-ip/MyWeb. But the Jquery post in above will post its data to a URL something like -your-iis-server-ip/Home/CreatePerson

To avoid this you need to create post URL dynamically using @Url.Action() helper. Here is the corrected JQuery post.

$.post("@Url.Action("CreatePerson", "Home")", { name: "John", time: "2pm" },
    function(data) {
    alert(data);
});

Sunday, June 26, 2011

Ncover Installation in windows server 2008

Recently I was trying to install Ncover in Windows Server 2008. Even though I had installed .Net framework 4.0 Ncover setup threw an error saying that "you need to have .Net 3.5 or above to install NCover".I had re installed .Net Framework 4.0 several times ad restarted the server but no effect.
Then I went to the server manger panel and I saw a section called feature summery. In add features section you can add .Net framework 3.5 features. After a restart everything worked fine.

Tuesday, June 21, 2011

Ncover Command Line For MSTest

If you are using MSTest here is the command windows batch command to generate test results

"C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\mstest.exe" /testcontainer: \yourTestProjectPath\YourTests.dll /testcontainer: \yourTestProjectPath2\YourTests2.dll /resultsfile:results.trx /testsettings:YourTestSettingsPath \local.testsettings


You can add several test projects using /testcontainer: argument. Here I have used two test projects. To use NCover we need to change this command by adding NCover commands . NCover arguments are started with “//” and for MSTest they started with “/”. Here Is the Ncover command to generate coverage.nccov and coverage.trend files.

"C:\Program files (x86)\NCover\NCover.Console.exe" "C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\mstest.exe" //x mstest_coverage.nccov //at "coverage.trend" /testcontainer: \yourTestProjectPath\YourTests.dll /testcontainer: \yourTestProjectPath2\YourTests2.dll /resultsfile:results.trx /testsettings:YourTestSettingsPath \local.testsettings


There are more options with NCover you can find those here Ncover command line

Now we have coverage.nccov and coverage.trend files we need to generate a HTML report from these two. For that we can use Ncover Reportng tool. Here is the Command to generate NCover HTML reports.

ncover.reporting mstest_coverage.nccov //lt coverage.trend //or FullCoverageReport:Html:output

Here we are generating full Coverage report in the directory called output