How to Perform CRUD Operations in ADO.NET Data Services Using Ajax Client

October 24, 2008

How to Perform CRUD Operations in ADO.NET Data Services Using Ajax Client

In the last post inHow to Perform CRUD Operations in ADO.NET Data Services Using Ajax Client
the ADO.NET data
services
series I wrote
about how you can
consume data services
using Ajax client. In this post I’m going to explain how to perform CRUD
operations
on a data service using an Ajax client.

DataServiceContext and Sys.Data.DataService

Before starting to understand how to perform CRUD operations with Ajax
client
first I want to discuss the main difference between using
DataServiceContext, like I did in previous posts, and using
Sys.Data.DataService. When we use the DataServiceContext on the server
side the context caches all the CRUD operations that we make and the only
way to submit these changes is by using the SaveChanges method. In the
Sys.Data.DataService we don’t have this unit of work functionality by default.
Every operation that we perform on the Sys.Data.DataService object is
immediately submitted to the server. There is a way to cache the operations
using an action sequence but I’ll describe it in the next post.
Another difference is that in the Ajax situation we are using the JSON
functionality in order to build the objects on the client side
and submit them to the server. We can’t use the server classes because the 
client doesn’t know them.

The Example Data Service Context

In the example I’m using the same data service context I built in the post
How to Perform CRUD Operations in ADO.NET Data Services Using a Custom Provider.
I changed the code that I wrote a little by adding the functionality to the
ResetResource method and the code looks like:

    [DataServiceKey("CourseID")]

    public class Course

    {

        #region Properties

 

        /// <summary>

        /// The course ID

        /// </summary>             

        public int CourseID { get; set; }

 

        /// <summary>

        /// The course title

        /// </summary>       

        public string Title { get; set; }

 

        /// <summary>

        /// The duration in days of the course

        /// </summary>

        public int Days { get; set; }

 

        /// <summary>

        /// The course academic credit

        /// </summary>

        public int Credit { get; set; }

 

        #endregion

 

        #region Methods

 

        /// <summary>

        /// Return a fixed list of courses

        /// </summary>

        /// <returns>A list of courses</returns>

        public static List<Course> GetCourses()

        {

            return new List<Course> {

                new Course() { CourseID = 1, Credit = 3, Days = 3 , Title = "Algorithms"},

                new Course() { CourseID = 2, Credit = 5, Days = 5 , Title = "Data Structures"},

                new Course() { CourseID = 3, Credit = 5, Days = 5 , Title = "Databases"}

            };

        }

 

        #endregion

    }

 

    public class CoursesDataContext : IUpdatable

    {

        #region Members

 

        private static List<Course> _courses;

 

        #endregion

 

        #region Properties

 

        /// <summary>

        /// Return the courses in a IQueryable format

        /// </summary>

        public IQueryable<Course> Courses

        {

            get

            {

                return _courses.AsQueryable();

            }

        }

 

        #endregion

 

        #region Ctor

 

        /// <summary>

        /// Construct a new CoursesDataContext object

        /// </summary>

        static CoursesDataContext()

        {

            _courses = Course.GetCourses();

        }

 

        #endregion

 

        #region IUpdatable Members

 

        public object CreateResource(string containerName, string fullTypeName)

        {

            // create the object using reflection

            var objType = Type.GetType(fullTypeName);

            var resourceToAdd = Activator.CreateInstance(objType);

 

            // add the course to the courses in-memory list

            _courses.Add((Course)resourceToAdd);

            return resourceToAdd;

        }

 

        public void DeleteResource(object targetResource)

        {

            // remove the course form the courses list

            _courses.Remove((Course)targetResource);

        }

 

        public object GetResource(IQueryable query, string fullTypeName)

        {

            object result = null;

            var enumerator = query.GetEnumerator();

            while (enumerator.MoveNext())

            {

                if (enumerator.Current != null)

                {

                    result = enumerator.Current;

                    break;

                }

            }

            if (fullTypeName != null &&

                !fullTypeName.Equals(result.GetType().FullName))

            {

                throw new DataServiceException();

            }

 

            return result;

        }

 

        public object GetValue(object targetResource, string propertyName)

        {

            // get the property info using reflection

            var targetType = targetResource.GetType();

            var targetProperty = targetType.GetProperty(propertyName);

 

            // retrun the value of the property

            return targetProperty.GetValue(targetResource, null);

        }

 

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

        {

            // get the property info using reflection

            Type targetType = targetResource.GetType();

            PropertyInfo property = targetType.GetProperty(propertyName);

 

            // set the property value

            property.SetValue(targetResource, propertyValue, null);

        }

 

        public object ResolveResource(object resource)

        {

            // nothing to do just return the resource

            return resource;

        }

 

        public void SaveChanges()

        {

            // object in memory – do nothing

        }

 

        public object ResetResource(object resource)

        {           

            var resourceType = resource.GetType();

 

            // get the list of attributes of the type

            object[] attributes = resourceType.GetCustomAttributes(true);

 

            // get the data service key attribute if exists

            var keyAttribute = attributes.First<object>(

                new Func<object, bool>(

                    attribute => attribute.GetType().Equals(typeof(DataServiceKeyAttribute))));

 

            if (keyAttribute != null)

            {

                var resourceKeyNames = ((DataServiceKeyAttribute)keyAttribute);

                foreach (var propertyInfo in resourceType.GetProperties())

                {

                    if (!resourceKeyNames.KeyNames.Contains(propertyInfo.Name))

                    {

                        // if the property name isn’t part of the keys colletion

                        // then set it to default value

                        var propType = propertyInfo.PropertyType;

                        object defaultValue =

                            propType.IsValueType

                            ? Activator.CreateInstance(propType)

                            : null;

                        propertyInfo.SetValue(resource, defaultValue, null);

                    }

                }

                return resource;

            }

 

            // no key

            return new Course();

        }

 

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

        {           

            throw new NotImplementedException();

        }

 

        public void ClearChanges()

        {          

            throw new NotImplementedException();

        }

 

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

        {

            throw new NotImplementedException();

        }

 

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

        {

            throw new NotImplementedException();

        }

 

        #endregion

    }

I made that change because in the update operation of Ajax client the method
resets the data in the resource in order to fill it with the updated data.
The data service code stays the same.

Performing CRUD Operations on Data Service Using Ajax Client

I added to the example from the last post more functionality in order to show
how to perform the CRUD operations. I added three buttons to the web form
I built and it’s code is now:

    <form id="form1" runat="server">

      <asp:ScriptManager ID="ScriptManager1" runat="server">

        <Scripts>

          <asp:ScriptReference Name="MicrosoftAjaxDataService.js" />

        </Scripts>

      </asp:ScriptManager>

      <div>

        <select id="ddlCourses">

        </select>

        <span id="spanError" style="display: none;"></span>

        <br />

        <input id="btnFill" type="button" value="Fill Drop Down List" onclick="Fill();" />

        <input id="btnInsert" type="button" value="Insert Course" onclick="Create();" />

        <input id="btnUpdate" type="button" value="Update Course" onclick="Update();" />

        <input id="btnDelete" type="button" value="Delete Course" onclick="Delete();" />

      </div>

 

      <script type="text/javascript">

        var proxy = new Sys.Data.DataService("/Services/CoursesService.svc");

 

        function Fill() {

           proxy.query("Courses", OnSuccess, OnFailure);

        }

 

        function OnSuccess(result, context, operation) {

           if (result != null) {

              var select = $get("ddlCourses");

              var option;

              for (i = 0; i < result.length; i++) {

                 option = new Option(result[i].Title, result[i].CourseID);

                 select.options.add(option);

              }

           }

        }

 

        function OnFailure(error, context, operation) {

            var span = $get("spanError");

            span.innerHTML = BuildErrorString(operation);

            span.style.display = "inline";

        }

 

        function BuildErrorString(operation) {

            var sb = new Sys.StringBuilder("Error occurred while performing operation ");

            sb.append(operation);

            sb.append(".");

            return sb.toString();

        }

 

        function Clear() {

            var select = $get("ddlCourses");

            for (i = select.length; i >= 0; i–) {

                select.remove(i);

            }

        }

 

        function OnOperationSuccess(result, context, operation) {

            if (result != null) {

                Clear();

                Fill();

            }

        }

 

        function Create() {

            var course = { Title: "Introduction to Computer science", Days: 5, Credit: 5 };

            proxy.insert(course, "Courses", OnOperationSuccess, OnFailure);

        }

 

        function Update() {

            proxy.query("Courses(1)", OnQueryForUpdateSuccess, OnFailure);

        }

 

        function Delete() {

            proxy.remove(null, "Courses(1)", OnOperationSuccess, OnFailure);

        }

 

        function OnQueryForUpdateSuccess(result, context, operation) {

            if (result != null) {

                result.Title = "Introduction";

                proxy.update(result, "Courses(1)", OnOperationSuccess, OnFailure);

            }

        }

      </script>

 

    </form>

Create

When we want to create an object and add it, we use the
Sys.Data.DataService object’s insert method.
The insert method gets six parameters:

proxy.insert(course, "Courses", OnOperationSuccess, OnFailure, context, true);

The parameters are:

  • The inserted object
  • The resource set Uri
  • The success callback function
  • The failure callback function
  • The user context
  • A flag to indicate if we want a return data which will return in the 
    success callback. The default value is true so you can omit that variable
    if you want the inserted data to be returned.

Behind the scenes the HTTP POST method will be called to perform the create
operation. In my example the Create method creates a new course and add it to
the Courses list on the server. 

Update

When we want to update a resource we need to have two things – the
resource that we want to update and the resource Uri. Without those two
you won’t be able to update the resource.
We use the Sys.Data.DataService object’s update method.
The update method gets the same six parameters like the insert method:

proxy.update(result, "Courses(1)", OnOperationSuccess, OnFailure, context, true);

Using the update method will cause an HTTP PUT request to the server.
In my example the Update method first searches a known resource and then
performs the update.

Delete

When we want to delete a resource we need to have one of two things –
the resource to delete or it’s resource Uri.
We use the Sys.Data.DataService object’s remove method.
The remove method take five parameters instead of six:

proxy.remove(course, "Courses(1)", OnOperationSuccess, OnFailure, context);

The parameters are:

  • The removed object
  • The resource set Uri of the removed object
  • The success callback function
  • The failure callback function
  • The user context

The two parameters are exclusively optional and you can use the first alone,
the second alone or both of them.
Using the remove method will cause an HTTP DELETE request to the server.
In my example the Delete method removes the first resource from the list
on the server. If you’ll press the delete button again it will cause an exception
because that resource was omitted.

Summary

Lets sum up, in the post I explained how you can perform CRUD operations
from the client side using Ajax. These operations are part of the
Sys.Data.DataService’s API and are very useful if you want to manipulate
your data from client side. As I wrote, the operations are performed immediately
after you call them and therefore you don’t have a unit of work functionality.
In the next post in the series I’m going to show how to cache and submit the
operations you perform using an action sequence

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>