Monday 17 February 2014

Consuming ASP.NET Web API OData Batch Update From JavaScript

Consuming OData with plain JavaScript is a bit painful, as we would require handling some of the low-level conversions. datajs is a JavaScript library that simplifies this task.

datajs converts data of any format that it receives from the server to an easily readable format. The batch request sent is a POST request to the path /odata/$batch, which is made available if batch update option is enabled in the OData route configuration.

As seen in the last post, a batch update request bundles of a number of create put and patch requests. These operations have to be specified in an object literal. Following snippet demonstrates it with a create, an update and a patch request:


var requestData = {
    __batchRequests: [{
        __changeRequests: [
            {
                requestUri: "/odata/Customers(1)",
                method: "PATCH",
                data: {
                    Name: "S Ravi Kiran"
                }
            },
            {
                requestUri: "/odata/Customers(2)"
                data: {
                    Name: "Alex Moore",
                    Department: "Marketing",
                    City:"Atlanta"
                }
            },
            {
                requestUri: "/odata/Customers",
                method: "POST",
                data: {
                    Name: "Henry",
                    Department: "IT",
                    City: "Las Vegas"
                }
            }
        ]
    }]
};

Following snippet posts the above data to the /odata/$batch endpoint and then extracts the status of response of each request:

OData.request({
    requestUri: "/odata/$batch",
    method: "POST",
    data: requestData,
    headers: { "Accept": "application/atom+xml"}
}, function (data) {
    for (var i = 0; i < data.__batchResponses.length; i++) {
        var batchResponse = data.__batchResponses[i];
        for (var j = 0; j < batchResponse.__changeResponses.length; j++) {
            var changeResponse = batchResponse.__changeResponses[j];
        }
    }
    alert(window.JSON.stringify(data));
}, function (error) {
    alert(error.message);
}, OData.batchHandler);


Happy coding!

Sunday 16 February 2014

Batch Update Support in ASP.NET Web API OData

The ASP.NET Web API 2 OData includes some new features including the support for batch update. This features allows us to send a single request to the OData endpoint with a bunch of changes made to the entities and ask the service to persist them in one go instead of sending individual request for each change made by the user. This reduces the number of round-trips between the client and the service.

To enable this option, we need to pass an additional parameter to the MapODataRoute method along with other routing details we discussed in an older post. Following statement shows this:


GlobalConfiguration.Configuration.Routes.MapODataRoute("ODataRoute", "odata", model,new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer));


I have a CustomerController that extends EntitySetController and exposes OData API to perform CRUD and patch operations on the entity Customer, which is stored in a SQL Server database and accessed using Entity Framework. Following is code of the controller:

public class CustomersController : EntitySetController<Customer, int>
{
    CSContactEntities context;

    public CustomersController()
    {
        context = new CSContactEntities();
    }

    [Queryable]
    public override IQueryable<Customer> Get()
    {
        return context.Customers.AsQueryable();
    }

    protected override int GetKey(Customer entity)
    {
        return entity.Id;
    }

    protected override Customer GetEntityByKey(int key)
    {
        return context.Customers.FirstOrDefault(c => c.Id == key);
    }

    protected override Customer CreateEntity(Customer entity)
    {
        try
        {
            context.Customers.Add(entity);
            context.SaveChanges();
        }
        catch (Exception)
        {
            throw new InvalidOperationException("Something went wrong");
        }

        return entity;
    }

    protected override Customer UpdateEntity(int key, Customer update)
    {
        try
        {
            update.Id = key;
            context.Customers.Attach(update);
            context.Entry(update).State = System.Data.Entity.EntityState.Modified;

            context.SaveChanges();
        }
        catch (Exception)
        {
            throw new InvalidOperationException("Something went wrong");
        }

        return update;
    }

    protected override Customer PatchEntity(int key, Delta<Customer> patch)
    {
        try
        {
            var customer = context.Customers.FirstOrDefault(c => c.Id == key);

            if (customer == null)
                throw new InvalidOperationException(string.Format("Customer with ID {0} doesn't exist", key));

            patch.Patch(customer);
            context.SaveChanges();
            return customer;
        }
        catch (InvalidOperationException ex)
        {
            throw ex;
        }
        catch (Exception)
        {
            throw new Exception("Something went wrong");
        }
    }

    public override void Delete(int key)
    {
        try
        {
            var customer = context.Customers.FirstOrDefault(c => c.Id == key);

            if (customer == null)
                throw new InvalidOperationException(string.Format("Customer with ID {0} doesn't exist", key));

            context.Customers.Remove(customer);
            context.SaveChanges();
        }
        catch (InvalidOperationException ex)
        {
            throw ex;
        }
        catch (Exception)
        {
            throw new Exception("Something went wrong");
        }
    }
}


Let’s use the batch update feature in a .NET client application. Let’s try adding a new customer and update an existing customer in the Customers table. Following code does this:

Container container = new Container(new Uri("http://localhost:<port-no>/odata"));

var firstCustomer = container.Customers.Where(c => c.Id == 1).First();
firstCustomer.Name = "Ravi Kiran";
container.UpdateObject(firstCustomer);

var newCustomer = new Customer() { Name="Harini", City="Hyderabad", Department="IT"};
container.AddToCustomers(newCustomer);

var resp = container.SaveChanges(SaveChangesOptions.Batch);


The last statement in the above snippet sends a batch request containing two requests: one to add a new customer and one to update an existing customer. The update operation calls the patch operation exposed by the OData API.

In next post, we will see how to perform batch update in a JavaScript client.

Happy coding!