Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pass class from MVC to Web API

In Web API side, I have a customer class like this

public class CustomerAPI
{  
    public string CustomerName { get; set; }
    public string CustomerCity { get; set; }
}

In MVC side I have a customer class like this

public class CustomerMVC
{
    public string CustomerName { get; set; }
    public string CustomerCity{ get; set; }
}

I`m consuming Web API services in ASP.Net MVC4 like below:

 var task = client.GetAsync("api/values")
                  .ContinueWith((taskwithresponse) =>
                    {
                        var response = taskwithresponse.Result;
                        var readtask = response.Content.ReadAsAsync<IEnumerable<CustomerMVC>>();

                        readtask.Wait();
                        serviceList = readtask.Result.ToList();
                    });
 task.Wait();  

I'm getting aggregate exception on doing this, How can I convert CustomerWebAPI to CustomerMVC.

like image 750
john Avatar asked Nov 05 '13 20:11

john


People also ask

How pass data from MVC controller to Web API?

From the start window select "Installed" -> "Visual C#" -> "Web". Select "ASP.NET MVC4 Web Application" and click on the "OK" button. From the "MVC4 Project" window select "Web API". Click on the "OK" button.

Can I use MVC controller as Web API?

In order to add a Web API Controller you will need to Right Click the Controllers folder in the Solution Explorer and click on Add and then Controller. Now from the Add Scaffold window, choose the Web API 2 Controller – Empty option as shown below. Then give it a suitable name and click OK.

How do I pass complex data to Web API?

Method 2: Using Newtonsoft JArraydll to your ASP.NET MVC project and WebAPI as well. Now, on Web API controller side, you will get your complex types within JArray as shown below. In this way, you can easily pass your complex types to your Web API. There are two solution, there may be another one as well.


2 Answers

It might help to split your code up a bit. I also recommend using the Newtonsoft.Json nuget package for serialization.

var task = client.GetAsync("api/values").Result;
//get results as a string
var result = task.Content.ReadAsStringAsync().Result;
//serialize to an object using Newtonsoft.Json nuget package
var customer = JsonConvert.DeserializeObject<CustomerMVC>(result);

If you wanted to make it asynchronous you could use the async and await keywords in C#5:

public async Task<CustomerMVC> GetCustomer()
{
    //return control to caller until GetAsync has completed
    var task = await client.GetAsync("api/values");
    //return control to caller until ReadAsStringAsync has completed
    var result = await task.Content.ReadAsStringAsync()
    return JsonConvert.DeserializeObject<CustomerMVC>(result);
}
like image 75
James Avatar answered Sep 20 '22 02:09

James


The deserialization solution feels like a bit of a hack here. Unless there's something you left out, you were probably running into an UnsupportedMediaTypeException which was showed up as an AggregateException because this is how uncaught Task exceptions rear their ugly heads.

Deserialization can be an expensive operation and with this solution you will end up taking the full hit every time you deserialize the object. Using response.Content.ReadAsAsync<IEnumerable<CustomerWebAPI>>() would be far more efficient due to a recent performance improvement to the ReadAsAsync extensions: http://blogs.msdn.com/b/webdev/archive/2015/02/09/asp-net-mvc-5-2-3-web-pages-3-2-3-and-web-api-5-2-3-release.aspx

As for converting from CustomerWebAPI to CustomerMVC, you could easily add a static convenience method like so:

public static CustomerMVC FromCustomerWebAPI(CustomerWebAPI customer){
    return new CustomerMVC(){
        CustomerName = customer.CustomerName,
        CustomerCity = customer.CustomerCity
    }
}

It's extra code, but should end up being far more efficient. If the customer object is a fairly large object, you can always use a tool like AutoMapper or ValueInjecter or you could just roll your own solution by caching the get (type you're mapping from) and set accessors (types you're mapping to) so you only have to incur the cost of reflection once - you would do this by compiling an expression - here's an example as to how you could do that for the Set accessors:

public static Action<object, object> BuildSetAccessor( MethodInfo method )
{
    var obj = Expression.Parameter(typeof(object), "o");
    var value = Expression.Parameter(typeof(object));

    Expression<Action<object, object>> expr =
        Expression.Lambda<Action<object, object>>(
               Expression.Call(
                      Expression.Convert( obj, method.DeclaringType )
                    , method
                    , Expression.Convert( value, method.GetParameters()[0].ParameterType )
            ), obj
              , value );

    return expr.Compile();
}
like image 36
Jordan Avatar answered Sep 21 '22 02:09

Jordan