Astoria service allows reading/querying of data via the already-established IQueryable interface – this helps in abstracting Astoria from the underlying data source. But there is no existing interface for the update operations (CUD – create, update, delete operations). Hence we came up with IUpdatable interface to support CUD operations and support read-write services.

One of the main design goals while designing the IUpdatable interface was to make it resource independent. In other words, the methods that return objects representing resources can return anything – for Astoria, the returned object is a opaque object that represents the resource being asked, and whenever we want to use the resource (reading/updating a value from the resource), we will pass the same opaque back to IUpdatable. The actual implementation of IUpdatable needs to track the mapping between this opaque object to the actual object it represents. The only time we need the actual clr instance of the resource is when we need to serialize the object and we call a specify method on IUpdatable (ResolveResource) for that.

Let take a quick look at IUpdatable interface

   

public interface IUpdatable

    {

/// <summary>

/// Creates the resource of the given type and belonging to the given container

/// </summary>

/// <param name="containerName">container name to which the resource belongs</param>

/// <param name="fullTypeName">full type name i.e. Namespace qualified type name of the resource</param>

/// <returns>object representing a resource of given type and belonging to the given container</returns>

object CreateResource(string containerName, string fullTypeName);

 

 

/// <summary>

/// Gets the resource of the given type that the query points to

/// </summary>

/// <param name="query">query pointing to a particular resource</param>

/// <param name="fullTypeName">full type name i.e. Namespace qualified type name of the resource</param>

/// <returns>object representing a resource of given type and as referenced by the query</returns>

object GetResource(IQueryable query, string fullTypeName);

 

 

/// <summary>

/// Gets the resource of the given type that the query points to. The resource returned contains the default values,

/// and not the value as present in the server

/// </summary>

/// <param name="query">query pointing to a particular resource</param>

/// <param name="fullTypeName">full type name i.e. Namespace qualified type name of the resource</param>

/// <returns>object representing a resource of given type and belonging to the given container and containing default values</returns>

object ReplaceResource(IQueryable query, string fullTypeName);

 

 

/// <summary>

/// Sets the value of the given property on the target object

/// </summary>

/// <param name="targetResource">target object which defines the property</param>

/// <param name="propertyName">name of the property whose value needs to be updated</param>

/// <param name="propertyValue">value of the property</param>

void SetValue(object targetResource, string propertyName, object propertyValue);

 

 

/// <summary>

/// Gets the value of the given property on the target object

/// </summary>

/// <param name="targetResource">target object which defines the property</param>

/// <param name="propertyName">name of the property whose value needs to be updated</param>

/// <returns>the value of the property for the given target resource</returns>

object GetValue(object targetResource, string propertyName);

 

 

/// <summary>

/// Sets the value of the given reference property on the target object

/// </summary>

/// <param name="targetResource">target object which defines the property</param>

/// <param name="propertyName">name of the property whose value needs to be updated</param>

/// <param name="propertyValue">value of the property</param>

void SetReference(object targetResource, string propertyName, object propertyValue);

 

 

/// <summary>

/// Adds the given value to the collection

/// </summary>

/// <param name="targetResource">target object which defines the property</param>

/// <param name="propertyName">name of the property whose value needs to be updated</param>

/// <param name="resourceToBeAdded">value of the property which needs to be added</param>

void AddReferenceToCollection(object targetResource, string propertyName, object resourceToBeAdded);

 

 

/// <summary>

/// Removes the given value from the collection

/// </summary>

/// <param name="targetResource">target object which defines the property</param>

/// <param name="propertyName">name of the property whose value needs to be updated</param>

/// <param name="resourceToBeRemoved">value of the property which needs to be removed</param>

void RemoveReferenceFromCollection(object targetResource, string propertyName, object resourceToBeRemoved);

 

 

/// <summary>

/// Delete the given resource

/// </summary>

/// <param name="targetResource">resource that needs to be deleted</param>

void DeleteResource(object targetResource);

 

 

/// <summary>

/// Saves all the pending changes made till now

/// </summary>

void SaveChanges();

 

 

/// <summary>

/// Returns the actual instance of the resource represented by the given resource object

/// </summary>

/// <param name="resource">object representing the resource whose instance needs to be fetched</param>

/// <returns>Returns the actual instance of the resource represented by the given resource object</returns>

object ResolveResource(object resource);

    }

 

Let’s go through each api one by one.

object CreateResource(string containerName, string fullTypeName) :– This is called when one tries to insert a new resource via the POST http method. The first parameter points to the container that the resource belongs to and the second parameter tells the namespace qualified name of the resource type that needs to be created. The second parameter might not be that useful when there is no inheritance, since from the container, one can easily figure out the type, but for inheritance cases, this is helpful. The return type, as said before, need not be the actual clr instance of the resource. It can be anything (a cookie) that only the IUpdatable implementor needs to understands.

object GetResource(IQueryable query, string fullTypeName) :- Get the given resource as resolved by the query and the namespace qualified name of the type that the query resolves to. In some cases, the full type name can be null. Look at the examples below to see the cases when the fullTypeName can be null. Again, the return type can be anything that represents the resource.

object ReplaceResource(IQueryable query, string fullTypeName) :- Very similar to the GetResource API, but this is used for replace-semantics and GetResource is used for merge-semantics. The implementation needs to return the resource referred by the query, but with default values for all non-key properties.

void SetValue(object targetResource, string propertyName, object propertyValue) :- Set the value of the property with the given name on the target resource to the given property value. The target resource is the opaque object returned by either CreateResource or GetResource. This method is called for scalar properties and complex properties only.

object GetValue(object targetResource, string propertyName) :- Gets the value of the property with the given name for the target resource. The target resource is the opaque object returned by either CreateResource or GetResource. This method is called for scalar properties or complex properties. If it’s a scalar property, we expect the returned object to be the actual value.

void SetReference(object targetResource, string propertyName, object propertyValue) :- This method is called for setting the value of navigation property with the given name on the target resource to the given propertyValue. The target resource and the propertyValue are opaque objects returned by GetResource or CreateResource API. This method is called for navigation properties that refer to a single resource – representing 0 or 0..1 side of a relationship.

void AddReferenceToCollection(object targetResource, string propertyName, object resourceToBeAdded):- This method is called for adding a resource to the collection navigation property with the given name on the target resource. Again, targetResource and resourceToBeAdded are opaque objects returned by GetResource or CreateResource API. The navigation property presents many side of a relationship. We generally call this operation as binding, which means you are binding the resourceToBeAdded to targetResource. In other words, you are setting up a relationship between the 2 resource objects.

void RemoveReferenceFromCollection(object targetResource, string propertyName, object resourceToBeRemoved):- Very similar to the above method, except it removes the given resource from the collection navigation property on the target object. We generally call this operation as unbinding, which means you are deleting the relationship between the two resource objects in question.

void DeleteResource(object targetResource):- This actually deletes the given resource. Again, the targetResource is the opaque object returned by GetResource or CreateResource API.

void SaveChanges():- This actually saves all the changes that has been made till now, using the above API’s. The IUpdatable implementation needs to track all changes until this API is called and then save all of them when this API is called. The IUpdatable implementation is expected to save all the changes or nothing

object ResolveResource(object resource):- This API is called whenever we want to resolve the opaque object returned by the CreateResource or GetResource API into the actual clr instance. This normally is called after SaveChanges, when we want to serialize out the resource (for POST methods). This method is also called if there are UpdateInterceptors, that needs to be invoked with the actual clr resource instances or the provider supports optimistic concurrency and the resource type has concurrency tokens (defined via etag properties in clr based provider).

This blog has already become bigger that I intended it to be. In my next blog, I will try and come up with some examples and the sequence of calls made on the IUpdatable interface for each of them. I have purposefully keep this post simple just so that people can get the basic idea of IUpdatable interface. There are few features like ETag, Update interceptors, etc due to which there might be additional calls on this interface. I will try and cover them in my coming blogs.

Pratik Patel

Developer, ADO.NET Data Services Framework

(Project Astoria)

This post is part of the transparent design exercise in the Astoria Team. To understand how it works and how your feedback will be used please look at this post.