Serialization Support for Delta Feed

This sample will introduce how to create a Delta Feed which is serialized into a Delta Response in Web API OData V4.

Similar to EdmEntityObjectCollection, Web API OData V5.6 now has an EdmChangedObjectCollection to represent a collection of objects which can be a part of the Delta Feed. A delta response can contain new/changed entities, deleted entities, new links or deleted links.

WebAPI OData V4 now has EdmDeltaEntityObject, EdmDeltaDeletedEntityObject, EdmDeltaLink and EdmDeltaDeletedLink respectively for the objects that can be a part of the Delta response. All the above objects implement the IEdmChangedObject interface, while the EdmChangedObjectCollection is a collection of IEdmChangedObject.

For example, if user defines a model as:

public class Customer
{
  public int Id { get; set; }
  public string Name { get; set; }
  public virtual IList<Order> Orders { get; set; }
}
public class Order
{
  public int Id { get; set; }
  public string ShippingAddress { get; set; }
}
private static IEdmModel GetEdmModel()
{
  ODataModelBuilder builder = new ODataConventionModelBuilder();
  var customers = builder.EntitySet<Customer>("Customers");
  var orders = builder.EntitySet<Order>("Orders");
  return builder.GetEdmModel();
}

The EdmChangedObjectCollection collection for Customer entity will be created as follows:

EdmChangedObjectCollection changedCollection = new EdmChangedObjectCollection(CustomerType); //IEdmEntityType of Customer

Changed or Modified objects are added as EdmDeltaEntityObjects:

EdmDeltaEntityObject Customer = new EdmDeltaEntityObject(CustomerType); 
Customer.Id = 123;
Customer.Name = "Added Customer";
changedCollection.Add(Customer);

Deleted objects are added as EdmDeltaDeletedObjects:

EdmDeltaDeletedEntityObject Customer = new EdmDeltaDeletedEntityObject(CustomerType);
Customer.Id = 123;
Customer.Reason = DeltaDeletedEntryReason.Deleted;
changedCollection.Add(Customer);

Delta Link is added corresponding to a $expand in the initial request, these are added as EdmDeltaLinks:

EdmDeltaLink CustomerOrderLink = new EdmDeltaLink(CustomerType);
CustomerOrderLink.Relationship = "Orders";
CustomerOrderLink.Source = new Uri(ServiceBaseUri, "Customers(123)");	
CustomerOrderLink.Target = new Uri(ServiceBaseUri, "Orders(10)");
changedCollection.Add(CustomerOrderLink);

Deleted Links is added for each deleted link that corresponds to a $expand path in the initial request, these are added as EdmDeltaDeletedLinks:

EdmDeltaDeletedLink CustomerOrderDeletedLink = new EdmDeltaDeletedLink(CustomerType);
CustomerOrderDeletedLink.Relationship = "Orders";
CustomerOrderDeletedLink.Source = new Uri(ServiceBaseUri, "Customers(123)");
CustomerOrderDeletedLink.Target = new Uri(ServiceBaseUri, "Orders(10)");
changedCollection.Add(CustomerOrderDeletedLink);

Sample for Delta Feed

Let’s create a controller to return a Delta Feed:

public class CustomersController : ODataController
{
  public IHttpActionResult Get()
  {
    EdmChangedObjectCollection changedCollection = new EdmChangedObjectCollection(CustomerType);
    EdmDeltaEntityObject Customer = new EdmDeltaEntityObject(CustomerType); 
    Customer.Id = 123;
    Customer.Name = "Added Customer";
    changedCollection.Add(Customer);
    
    EdmDeltaDeletedEntityObject Customer = new EdmDeltaDeletedEntityObject(CustomerType);
    Customer.Id = 124;
    Customer.Reason = DeltaDeletedEntryReason.Deleted;
    changedCollection.Add(Customer);

    EdmDeltaLink CustomerOrderLink = new EdmDeltaLink(CustomerType);
    CustomerOrderLink.Relationship = "Orders";
    CustomerOrderLink.Source = new Uri(ServiceBaseUri, "Customers(123)");
    CustomerOrderLink.Target = new Uri(ServiceBaseUri, "Orders(10)");
    changedCollection.Add(CustomerOrderLink);
    
    EdmDeltaDeletedLink CustomerOrderDeletedLink = new EdmDeltaDeletedLink(CustomerType);
    CustomerOrderDeletedLink.Relationship = "Orders";
    CustomerOrderDeletedLink.Source = new Uri(ServiceBaseUri, "Customers(123)");
    CustomerOrderDeletedLink.Target = new Uri(ServiceBaseUri, "Orders(11)");
    changedCollection.Add(CustomerOrderDeletedLink);
    return Ok(changedCollection);
  }
}

Now, user can issue a GET request as:

http://localhost/odata/Customers?$expand=Orders&$deltatoken=abc

The corresponding payload will has the following contents:

{
  "@odata.context":"http://localhost/odata/$metadata#Customers",
  "value": [
    {
      "Id":123,
      "Name":"Added Customer"
    },
    {
      "@odata.context":"http://localhost/odata/$metadata#Customers/$deletedEntity",
      "Id": 124
      "Reason":"Deleted"
    },
    {
      "@odata.context":"http://localhost/odata/$metadata#Customers/$link",
      "source":"Customers(123)",
      "relationship":"Orders",
      "target":"Orders(10)"
    },
    {
     	"@odata.context":"http://localhost/odata/$metadata#Customers/$deletedLink",
     	"source":"Customers(123)",
     	"relationship":"Orders",
     	"target":"Orders(11)"
    }
  ]
}