Click here to Skip to main content
Click here to Skip to main content

Passing Object Collections to ASP.NET Web API

, 16 Jan 2014
Rate this:
Please Sign up or sign in to vote.
POC to demonstrate passing of collections of complex objects to ASP.NET Web API Controllers.

Introduction

This is a POC to determine most simple way to pass collections of complex objects to ASP.NET Web API Controller. New methods are added to Controller and Data Repository to allow collections to be passed to Add and Update methods in request payload. To use solution (VS2012), unzip and insure NuGet auto-restore is on for project.

Background

I made a number of attempts to pass collections to Web API, using Json Serialization and Deserialization and/or container object to hold the collections with varying success. The updated versions of ASP.NET Web API allows for behind-the-scenes serialization that enable collections to be passed directly to controller methods as payload in POST or PUT messages. The attached VS2012 Solution demonstrates minimal coding required to allow single call to Web API Controller to process multiple objects. This should be useful for large sets of objects being inserted or updated at the same time, to minimize calls to the API Controller.

I based my test code on the following examples. These are basic ASP.NET Web API tutorial projects using a Product object passed singly to controller CRUD methods. I added a Console client project for testing new methods for Collections of Product Items instead of single items...here and here

I found an example of batching Web API calls but I haven't evaluated it. I think it uses separate calls for each command. With batching, calls to add, update, or delete items can be combined and can be individually monitored. Batching example can be found here [^].

Using the Code

I changed WebApiConfig to include method name. Register method in WebApiConfig should look like:

 public static void Register(HttpConfiguration config)
        {
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{action}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }

This is necessary to allow for multiple POST and PUT methods to be in the same controller. If route template is not adjusted, call to controller will generate an error saying there are multiple methods with same signature (it will just look at Http Verb, not method name). Simple controller method to post multiple Products:

public IEnumerable<Product> PostProducts([FromBody]IEnumerable<Product> pList)    
{
     return repository.AddProducts(pList);
}

Then in repository call the single-item Add(Product) method for each item in collection:

 // added for multiple posts using Product collection ProductList
        public IEnumerable<product> AddProducts(IEnumerable<Product> pList)
        {
            List<product>  pNewProducts = new List <product>();
            Product prod = new Product();
            foreach (Product p in pList)
            {
                prod = Get(p.Id); // get item being updated
                if (p.Equals(Add(p)))
                    pNewProducts.Add(p);
            }
            return pNewProducts; // return collection of added products
        } 

When updating a single item, the item id is passed in the URI query string. But in the Put method for updating records, no ID is sent as separate parameter in URI query string. Instead, each object contains its normal ID field for matching in repository:

public IEnumerable<product> PutProducts([FromBody]List<Product> pList)
        {
            return repository.UpdateProducts(pList);
        }

Created console client to test ProductController and repository methods. Below is test for adding multiple products -- a List<product> collection is created and passed in body of PostAsJsonAsync call to Web API.

Included in the Web API project are original MVC pages for single item add/update. I created the console client to test the adding or updating collections of Products. There is no multiple-item delete method but one could be easily added by passing either a collection of items to a new DELETE controller method as in the PUT or POST, or an array of id values could be passed and each id passed in turn to the original delete method. Note that to delete a collection of items, I think it's necessary to use POST or PUT and not DELETE as http action.

All dependencies have been removed but should be restored during build so long as auto-restore NuGet is activated.

 private static async Task<list<product>> postProducts () // add product collection
        {
            HttpClient client = new HttpClient();
            List<product> products = new List<product>();
            Product p1 = new Product(); 
            p1.Name = "Product4";
            p1.Price = 111.1M;
            p1.Category = "Condiments";
            products.Add(p1);
            Product p2 = new Product();
            p2.Name = "Product5";
            p2.Price = 222.2M;
            p2.Category = "Hardware";
            products.Add(p2);
            Product p3 = new Product();
            p3.Name = "Product6";
            p3.Price = 333.3M;
            p3.Category = "Toys";
            products.Add(p3);
            // need to add controller method name based on changes to routing in WebApiConfig.cs
            response = await client.PostAsJsonAsync<list
            <product>>(serviceUrl + @"/PostProducts", products);
            if (!response.IsSuccessStatusCode)
            {
                Console.WriteLine("ERROR:  Products Not Posted." + response.ReasonPhrase);
                return null;
            }
            products = await response.Content.ReadAsAsync<list<product>>();
            return products; // 
        }

If I can get to it, I'll set up a test to add, update, or delete large numbers of items to determine how much efficiency is gained by submitting a collection to an API controller compared to individual calls for each add/update/delete action.

If anyone has any questions or sees that this code could work better in any way, please let me know. Please bear in mind this is a proof of concept and NOT meant to be production-ready code.

Thanks for reading.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Tom Glick Philadelphia, US
Software Developer (Senior)
United States United States
Enterprise developer with over 15 years of experience coding Windows and Web Apps in C#, WCF, SQL, EF, Http, Linq, etc. Enjoy middle-tier development on large BLL/DAL projects, also full SDLC for smaller projects needing single developer.
 
Currently live near Philadelphia although I've been working for the past 20 months for a large health-care provider in Pittsburgh.

Comments and Discussions

 
QuestionFile not found on the source link? PinmemberMember 1053688420-Jan-14 11:31 
AnswerRe: File not found on the source link? PinprofessionalTom Glick Philadelphia, US20-Jan-14 12:36 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140721.1 | Last Updated 16 Jan 2014
Article Copyright 2014 by Tom Glick Philadelphia, US
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid