DotNetCurry Logo

ASP.NET Web API: Passing Multiple Objects as an Input Parameters to Action Method

Posted by: Mahesh Sabnis , on 4/19/2016, in Category ASP.NET
Views: 55689
Abstract: Posting multiple entity values to the ASP.NET Web API action method

Any client application ranging from Desktop, to devices on any platform can call a Web API and perform Data Read/Write operations. ASP.NET Web API provides action methods for HTTP GET, POST, PUT and DELETE operations. Generally, POST and PUT accepts a single model object (Entity) as input parameter, so that we can make a HTTP POST/PUT request to create a new entry or update an existing entry. But what if we want to make a Post request with multiple model objects?

This article targets Web API 2.0 and MVC 5. The article uses a scenario of Order and ItemDetails to explain the mechanism of passing multiple entities as input parameters to the Action Method of Web API. 

ASP.NET MVC CMS for Enterprise Development. Try for Free

Use Case

Our use case for our example is that we want to save both Order and Order Item Details in a single request to Web API. In this case, the Post action method of Web API needs to accept Order Entity object and an array of Item details as a second parameter. Let us see how to implement it.

Passing Multiple Objects to Web API - The implementation

Note: This implementation is done using Visual Studio 2015 Community Edition, although you can use VS 2013 too.

Step 1: Open Visual studio and create a new ASP.NET Web Application of name WEBAPI_MultipleParameters. Select an Empty MVC application with Web API enabled as shown in the following image

create-project

Step 2: This step will create a project. In this project, in the App_Data folder, add a new Sql Server database of name ApplicationDB.mdf.

Step 3: In the Models folder, add a new class file of name ModelClasses.cs. In this file add the following code:

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace WEBAPI_MultipleParameters.Models
{
    public class Order
    {
        [Key]
        public int OrderId { get; set; }
        public string CustomerName { get; set; }
        public ICollection<ItemDetails> Details { get; set; }
    }

    public class ItemDetails
    {
        [Key]
        public int ItemId { get; set; }
        public string ItemName { get; set; }
        public int Quantity { get; set; }
        public int UnitPrice { get; set; }
        public int OrderId { get; set; }
        public Order Order { get; set; }
    }
}

The above code contains Order and ItemDetails class. There is one-to-many relationship defined in both classes.

Step 4: In the Models folder, add a new ADO.NET Entity Data Model, name this as OrderEntities. This will star the wizard, in this wizard, select Code First From Database options as shown in the following image

ef-code-first

Select the ApplicationDB.mdf in the wizard. When we finish this wizard, the Models folder will be added with OrderEntities class. In this class add the following two properties which represents mapping between Model classes added in Step 3.

public DbSet<Order> Orders { get; set; }
public DbSet<ItemDetails> ItemDetails { get; set; }

Step 5: In the Controllers folder add a new Empty Web API Controller class with name as OrderAPIController. In this class add the following action method

public IHttpActionResult Post(Order order, ItemDetails [] details)
{
    Order ord = order;
    var ordDetails = details;
    return Ok();
}

The above action method accepts Order and ItemDetails as input parameters.

Open Fiddler (or Postman) and create a Post request for this action method as shown in the following image

fiddler-post

We have created a Request Body accepting two JSON objects Order and ItemDetails. Click on the Execute button, we will get Error 500, that is Internal Server Error. If we see the TextView details of error then the following result will be displayed.

text-view

It clearly states Can’t bind multiple parameters. This indicates that we cannot pass multiple entities as an input parameter to Web API action methods. But as per the use case discussed in the Requirement section, we want to implement the same. Let us see what changes must be done within the Action method of WEB API.

Approach 1: Using ViewModel object

Step 6: In this case, one of the possible solutions is to create a ViewModel class which defines public properties - one for Order class and another property of an array type of ItemDetails class as shown in the following code:

public class OrderItemDetailsViewModel
{
    public Order order { get; set; }
    public ItemDetails[] itemDetails { get; set; }
}

Step 7: Change the Post action method as shown in the following code:

public IHttpActionResult Post(OrderItemDetailsViewModel orderInfo)
{
    Order ord = orderInfo.order;
    var ordDetails = orderInfo.itemDetails;
    return Ok();
}

The action method accepts an OrderItemDetailsViewModel class object.

Step 8: Apply a breakpoint on the Post action method and run the application and use the Fiddler tool for the Post request. The Post action will now accepts the data as shown in the following image

viewmodel-approach

The above image shows the Order and ItemDetails values accepted.

One of the limitation of this approach is, if we have several post requests with multiple parameters in our Web API application, then we need to create several ViewModel classes, e.g. Order and Payment entities. In such cases, our application will have several ViewModel objects and some of them will be used few times only.

Update: Taseer made a very valid point in the comments section. He says "I recommend going with approach number one, if you are going to change the request body then API consumers will be affected, and this is not something very dynamic you do always. As well if you want to generate documentation for your API using automated tools such as Swashbuckle; then passing JObject in the request body won't work correctly. So IMO using DTOs or Models is a cleaner approach."

I completely agree to what he says. Approach 2 must be treated as a source of information that conveys an alternate way of achieving the same result. Approach 1 is the preferred approach. 

Approach 2: Using Json.Net with Newtonsoft.Json

JSON.Net is an excellent framework used in .NET applications using which we can easily read JSON data and manage serialization and deserialization of JSON data. We can handle our use case using JObject. This represent the JSON object and this can be used to read JSON data posted using Http request.

In the Web API application we already have a reference to Newtonsoft.Json.

Step 9: We will modify the Post method as shown in the following code:

public class OrderAPIController : ApiController
{
    OrderEntities ctx;

    public OrderAPIController()
    {
        ctx = new OrderEntities(); 
    }
    public IHttpActionResult Post(JObject objData)
    {
        List<ItemDetails> lstItemDetails = new List<ItemDetails>();
        //1.
        dynamic jsonData = objData;
        //2.
        JObject orderJson = jsonData.order;
        //3.
        JArray itemDetailsJson = jsonData.itemDetails;
        //4.
        var Order = orderJson.ToObject<Order>();
        //5.
        foreach (var item in itemDetailsJson)
        {
            lstItemDetails.Add(item.ToObject<ItemDetails>());
        }
        //6.
        ctx.Orders.Add(Order);
        //7.
        foreach (ItemDetails itemDetail in lstItemDetails)
        {
            ctx.ItemDetails.Add(itemDetail);
        }

        ctx.SaveChanges();

        return Ok();
    }
}

The above method has the following specifications (Note: Following line numbers map with comments applied in the Post method.)

1. The received data to the post method is stored in the dynamic object.

2. We are reading the order key data from the posted data in the JObject object.

3. The itemDetails received from the request will be stored in JArray - this is the JSON array.

4. The received order data is deserialized in the Order object.

5. The itemDetails are deserialized into the List of ItemDetails object.

6. Step 6 and 7 stores data in the database.

Step 10: Apply a breakpoint on the Post method and run the application. Use Fiddler to post data to the method, the following result will be displayed.

jobject-aaproch

The JSON data is accepted using JObject.

Lets create a View for the application

Step 11: Using Manage NuGet Packages, add jQuery library in the project. In the Controllers folder, add a new Empty MVC controller of name OrderController. We will have an Index method in this controller. Scaffold an empty view from this action method. This will generate Index.cshtml view. In this view add the following script and markup

@{
ViewBag.Title = "Index";
}
< script src="~/scripts/jquery-2.2.0.min.js">
< script type="text/javascript">

$(document).ready(function () {

    $("#btn").on('click', function () {
        //1.
        var order = {
            "CustomerName":"MS"
        };
        //2.
        var itemDetails = [
            { "ItemName": "Desktop", "Quantity": 10, "UnitPrice": 45000 },
            { "ItemName": "Laptop", "Quantity": 30, "UnitPrice": 80000 },
            { "ItemName": "Router", "Quantity": 50, "UnitPrice": 5000 }
        ];
        //3.
        $.ajax({
            url: '/api/OrderAPI',
            type: 'POST',
            //4.
            data: {
                order: order,
                itemDetails: itemDetails
            },
            ContentType: 'application/json;utf-8',
            datatype:'json'
        }).done(function (resp) {
            alert("Successful " + resp);
        }).error(function (err) {
            alert("Error " + err.status);

        });

    });

});



<h2>Index</h2>

<input type="button" id="btn" value="Save" />

The above script contains the following specifications (Line numbers in the code map with comments applied in the Post method.)

1. Define an order object with CustomerName property.

2. Define a JavaScript array with itemDetails.

3. The ajax call defines data attribute with JSON object which contains order and itemDetails. These values will be posted to the Web API.

Update: Please note that we are posting Order and ItemDetails, the OrderId needed for ItemDetails will be received from the server when the Order entity is saved in the Orders table using SaveChanges() method. This means that all ItemDetails entered are against the same OrderId. That’s the reason we are not passing OrderId in ItemDetails records.   

Run the application and click on Save button, the data will be saved in the database.

Conclusion:

In my opinion, using JObject  ViewModel is a good approach to post multiple entities values to the Web API action method. Thanks to everyone who gave their valuable feedback in the comments section to make the article better and useful.

Download the entire source code of this article (Github)

Was this article worth reading? Share it with fellow developers too. Thanks!
Share on Google+
Further Reading - Articles You May Like!
Author
Mahesh Sabnis is a DotNetCurry author and Microsoft MVP having over 17 years of experience in IT education and development. He is a Microsoft Certified Trainer (MCT) since 2005 and has conducted various Corporate Training programs for .NET Technologies (all versions). Follow him on twitter @maheshdotnet


Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!