Monday, 28 November 2011

Passing Object in JSON to Controller

In this post, we will see how to pass object(s) in JSON from a jQuery Ajax function to a controller method in ASP.NET MVC 3. For more detailed explanation about the jQuery function and receiver controller method, you may want to see my previous post.

First we prepare our class which structure will be similar as the structure of the object(s) in JSON that is going to be passed. This class will also be the data type of the object(s) received by the receiver method through its parameter:
public class TeamViewModel
{
    public int TeamId { get; set; }
    public string Name { get; set; }
}

Then our JavaScript codes (jQuery version used at the time of writing is 1.5.1):
$(document).ready(function () { 
    //pass an object 
    $("#ajaxBtnPostTwo").click(function (event) {
        $.ajax({
            type: "POST",
            url: "/Teams/ProcessObjectUsingHttpPost",
            data: "{ 'TeamId':'10', 'Name':'TopTeam' }",
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            success: ObjectWithAjaxSucceeded,
            error: AjaxFailed
        });
    });

    //pass a collection of objects
    $("#ajaxBtnPostThree").click(function (event) {
        var teamlist = [ { TeamId: 5, Name: 'Team Five'},
                            { TeamId: 6, Name: 'Team 6'},
                            { TeamId: 7, Name: 'Team seven'} ]

        $.ajax({
            type: "POST",
            url: "/Teams/ProcessListObjectsUsingHttpPost",
            data: JSON.stringify(teamlist),
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            success: ObjectWithAjaxSucceeded,
            error: AjaxFailed
        });
    });
});

function ObjectWithAjaxSucceeded(data) {
    alert('success');
    //write content to a div
    $('#ajaxDiv').html(data);
}
function AjaxFailed(result) {
    alert('an error has occured: ' + result.status + ' ' + result.statusText);
    alert(result.responseText);
}
Note that the first ajax function is passing an object while the second one is passing a collection of objects. On the first method, the object is specified directly in the JSON string. While on the second method, JSON.stringify() method is used to convert the JavaScript objects into JSON string.

Finally, our controller methods:
[AcceptVerbsAttribute(HttpVerbs.Post)]
public JsonResult ProcessObjectUsingHttpPost(TeamViewModel team)
{
    return Json(String.Format("{0} -processed- <br/> {1} -processed-",
                            team.TeamId, team.Name));
}

[AcceptVerbsAttribute(HttpVerbs.Post)]
public JsonResult ProcessListObjectsUsingHttpPost(List<TeamViewModel> teams)
{
    StringBuilder sb = new StringBuilder();
    foreach (TeamViewModel team in teams)
    {
        sb.AppendFormat("{0} -processed- , {1} -processed <br />", team.TeamId, team.Name);
    }
    return Json(sb.ToString());
}
The first one receives a single object while the second one receives a collection of objects. The framework automatically serialize the JSON data into the data type (class) that we have specified above; ie. TeamViewModel.

On the next post we will see how to do Ajax call with HTTP GET method.

Monday, 21 November 2011

Ajax with jQuery and JSON in ASP.NET MVC 3

In this article, we'll see how to do Ajax request using HTTP POST method in ASP.NET MVC 3. The recent MVC framework allows this to be done easily.

First we prepare our html controls.
<input id="ajaxBtnPostOne" value="Ajax Button Post 1" param="1" type="button" />
<div id='ajaxDiv'></div>

Then the javascript codes. Make sure you have jQuery library in the application. In this article, I use jQuery 1.5.1.
$(document).ready(function () {
    $("#ajaxBtnPostOne").click(function (event) {
        $.ajax({
            type: "POST",
            url: "/Teams/GetDetailsUsingHttpPost", 
            data: "{ 'id':'" + $('#ajaxBtnPostOne').attr('param') + "'}",
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            success: AjaxSucceeded,
            error: AjaxFailed
        });
    });
});

function AjaxSucceeded(data) {
    alert('success');
    //write content to a div
    $('#ajaxDiv').html(data);
}

function AjaxFailed(result) {
    alert('an error has occured: ' + result.status + ' ' + result.statusText);
    alert(result.responseText);
}
Note that we use jQuery function $.ajax() to bind to the 'on clicked' event of the html button we have above.
The settings on this method:
- type: we want use HTTP POST.
- url: this is the url where the Ajax call will be posted to. In this case is the location of the server side method 'GetDetailsUsingHttpPost' that we are going to create below.
- data: this is the data that will be passed to the method.
- dataType: we want to use JSON in this case.
- success: what to do when the Ajax call is returned successfully, in this case we execute a method.
- error: what to do if an error occurred, in this case we execute a method.

Then we have the server side receiver method. In MVC, we can simply use a controller method to handle an Ajax call. No extra configuration is needed.
[AcceptVerbsAttribute(HttpVerbs.Post)]
public JsonResult GetDetailsUsingHttpPost(int id)
{
    Team team = teamRepository.Find(id);
    return Json("Team Name = " + team.Name);
}
Note that '[AcceptVerbsAttribute(HttpVerbs.Post)]' attribute is used so that the method will only respond to a POST method. When Ajax call is used with JSON to query sensitive information, it is recommended to use POST method. Please see http://haacked.com/archive/2009/06/25/json-hijacking.aspx for more details.

Also notice that the method accept an integer parameter however the passed data is a string; the framework does the conversion automatically. Note that the variable name ('id') used in the 'data' setting of the javascript function is the same as the one accepted by the method.

To return JSON data, we just need to put the data to be returned inside Json() method and specify the controller method's return type as JsonResult.

Say now we want to return an object instead of a string. To do this, we can simply use an anonymous type or a class. If we choose to use a class then prepare our class, eg. TeamViewModel:
public class TeamViewModel
{
    public int TeamId { get; set; }
    public string Name { get; set; }
}
No extra configuration is needed to return an anonymous type or a class' object to the Ajax call. Here is our modified controller method:
/*modified to return an object instead of string data*/
[AcceptVerbsAttribute(HttpVerbs.Post)]
public JsonResult GetDetailsUsingHttpPost(int id)
{
    Team team = teamRepository.Find(id);
    TeamViewModel teamVM = new TeamViewModel();
    teamVM.TeamId = team.TeamId;
    teamVM.Name = team.Name;
    return Json(teamVM);

    // we could also have used an anonymous type instead
    //var anonymousTeam = new { TeamId = team.TeamId, Name = team.Name, City = "sydney" };
    //return Json(anonymousTeam);
}
We need to change the js function that is called when the Ajax call is successful:
function AjaxSucceeded(objdata) {
    alert('success');
    alert(objdata.TeamId + ' - ' + objdata.Name);
    $('#ajaxDiv').html(objdata.TeamId + ' - ' + objdata.Name);
}
Notice that the received object has similar structure as the returned object's type from the controller method; ie. it has 'TeamId' and 'Name' members. It also preserve the case sensitivity of the properties; if we use small case letters 'objdata.teamid' instead of 'objdata.TeamId', this will be rendered as 'undefined'.

On the next post, we will see how to pass object(s) in the Ajax function.

Friday, 11 November 2011

More Advanced Use of AutoMapper - Part 2

Custom type converters
// Source and destination classes
public class Source
{
    public string Value1 { get; set; }
    public string Value2 { get; set; }
    public string Value3 { get; set; }
}
public class Destination
{
    public int Value1 { get; set; }
    public DateTime Value2 { get; set; }
    public Boolean Value3 { get; set; }
}


// Custom type converter classes
  //custom type converter class uses 'ITypeConverter' interface and has 'Convert' method
public class DateTimeTypeConverter : ITypeConverter<string, DateTime>
{
    public DateTime Convert(ResolutionContext context)
    {
        return System.Convert.ToDateTime(context.SourceValue);
    }
}
public class TypeTypeConverter : ITypeConverter<string, Boolean>
{
    public Boolean Convert(ResolutionContext context)
    {
        return System.Convert.ToBoolean(context.SourceValue);
    }
}


// Specify mappings
  //using .NET built in function
Mapper.CreateMap<string, int>().ConvertUsing(Convert.ToInt32);

  //using custom type converter classes
Mapper.CreateMap<string, DateTime>().ConvertUsing(new DateTimeTypeConverter());
Mapper.CreateMap<string, Boolean>().ConvertUsing<TypeTypeConverter>();

Mapper.CreateMap<Source, Destination>();


// Usage
var source = new Source
{
    Value1 = "5",
    Value2 = "01/01/2000",
    Value3 = "true"
};
Destination result = Mapper.Map<Source, Destination>(source);
With custom type converter, the mapping is applied automatically by AutoMapper when any source and destination types match. This mapping has a global scope.



Custom value resolvers
// Source and destination classes
public class SourceRsolvr
{
    public int Value1 { get; set; }
    public int Value2 { get; set; }
}
public class DestinationRsolvr
{
    public int Total { get; set; }
}


// Custom resolver classes
  //custom resolver class derives from 'ValueResolver' and overrides 'ResolveCore' method, we can also create a
  //custom resolver class that derives from 'IValueResolver' but this is rarely used

  //a custom resolver without argument
public class CustomResolverOne : ValueResolver<SourceRsolvr, int>
{
    protected override int ResolveCore(SourceRsolvr source)
    {
        return source.Value1 + source.Value2;
    }
}

  //a custom resolver with argument
public class CustomResolverTwo : ValueResolver<SourceRsolvr, int>
{
    private readonly Expression<Func<int, bool>> _func;

    public CustomResolverTwo(Expression<Func<int, bool>> func)
    {
        _func = func;
    }

    protected override int ResolveCore(SourceRsolvr source)
    {
        var list = new[] { source.Value1, source.Value2};
        return list.Where(_func.Compile()).Sum();
    }
}


// Mapping
  // using custom resolver without argument
Mapper.CreateMap<SourceRsolvr, DestinationRsolvr>()
        .ForMember(dest => dest.Total, 
                   opt => opt.ResolveUsing<CustomResolverOne>()
                             .ConstructedBy(() => new CustomResolverOne()));

  // another example of using custom resolver with argument
//Mapper.CreateMap<SourceRsolvr, DestinationRsolvr>()
//        .ForMember(dest => dest.Total, 
//                   opt => opt.ResolveUsing<CustomResolverTwo>()
//                             .ConstructedBy(() => new CustomResolverTwo(x => x > 5)));


// Usage
var source = new SourceRsolvr
{
    Value1 = 5,
    Value2 = 7
};
var result = Mapper.Map<SourceRsolvr, DestinationRsolvr>(source);
Unlike custom type converter, custom value resolver needs to be specified in the configuration of any destination class' member that would like to apply it. Thus it will only be implemented to specific members that are configured for it.

Reference:
https://github.com/AutoMapper/AutoMapper/wiki

Friday, 4 November 2011

More Advanced Use of AutoMapper - Part 1

Projection
public class Person
{
    public int PersonId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime DateOfBirth { get; set; }
}

public class PersonViewModel
{
    public string FullName { get; set; }
    public int DayOfBirth { get; set; }
    public int MonthOfBirth { get; set; }
    public int YearOfBirth { get; set; }
    public string Email { get; set; }
}


Mapper.CreateMap<Person, PersonViewModel>()
    .ForMember(d => d.DayOfBirth, o => o.MapFrom(s => s.DateOfBirth.Day))
    .ForMember(d => d.MonthOfBirth, o => o.MapFrom(s => s.DateOfBirth.Month))
    .ForMember(d => d.YearOfBirth, o => o.MapFrom(s => s.DateOfBirth.Year))
    .ForMember(d => d.FullName, o => o.MapFrom(s => s.FirstName + " " + s.LastName))
    .ForMember(d => d.Email, o => o.Ignore());
AutoMapper needs to know how to map to ALL of the destination class members. It is fine if some source class' members do not have any match. In this case the source class Person has a PersonId member, however it is not used in the destination class. No mapping configuration is required, AutoMapper will automatically ignore it. However for the destination class member Email which do not have a match in the source class, it is necessary to specify a mapping configuration.


Collections
After a map is created, we can map a collection of source class' objects into a collection of destination class' objects without extra configuration.
var sources = new[]
    {
        new Person{PersonId =10, DateOfBirth=new DateTime(1910,10,1), FirstName="Johnny", LastName="King"},
        new Person{PersonId =11, DateOfBirth=new DateTime(1911,11,2), FirstName="Katherine", LastName="Wood"},
        new Person{PersonId =12, DateOfBirth=new DateTime(1912,12,3), FirstName="Sam", LastName="Bourke"}
    };
List<PersonViewModel> listPersonViewModels = Mapper.Map<Person[], List<PersonViewModel>>(sources);
Collection types supported are: IEnumerable, IEnumerable<T>, ICollection, ICollection<T>, IList, IList<T>, List<T> and Arrays.


Nested mappings
No mapping configuration is needed to map a nested class. As long as all of the destination class' members have matches, we just need to specify one more mapping for each nested class.
public class Outer
{
    public int Value { get; set; }
    public Nested InnerClass { get; set; }
}
public class Nested
{
    public int InnerValue { get; set; }
}

public class OuterViewModel
{
    public int Value { get; set; }
    public NestedViewModel InnerClass { get; set; }
}
public class NestedViewModel
{
    public int InnerValue { get; set; }
}


Mapper.CreateMap<Outer, OuterViewModel>();
// need to specify mapping for the nested class as well
Mapper.CreateMap<Nested, NestedViewModel>();


// usage
var source = new Outer
{
    Value = 5,
    InnerClass = new Nested { InnerValue = 15 }
};
var dest = Mapper.Map<Outer, OuterViewModel>(source);

Reference:
https://github.com/AutoMapper/AutoMapper/wiki