How To Perform CRUD Operations in ADO.NET Data Services With Custom Provider

August 31, 2008

How To Perform CRUD Operations in ADO.NET Data Services With Custom Provider

After we learned how to consume an How To Perform CRUD Operations in ADO.NET Data Services
ADO.NET data service with a .Net
client in the previous post, the next
thing to learn is how to do CRUD
(Create, Update and Delete) operations
with the client we built. This post will
explain how to use these operations.

The IUpdatable Interface
In order to enable a a custom provider to do CRUD operations the data
context object need to implement the IUpdatable interface. That interface can be
found in the System.Data.Services namespace. The interface include a variety of
methods to implement and in the example I will show only a couple of those methods.
Returning to the example of the last post, I changed a bit the CoursesDataContext
to implement the IUpdatable interface and also to hold a static Course list which will
be the object that the data service will use.

The Data Context Example

    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 void SetReference(object targetResource,

           string propertyName, object propertyValue)

        {

            throw new NotImplementedException();

        }

 

        public object ResetResource(object resource)

        {

            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 didn’t implemented the following methods – RemoveReferenceFromCollection,
AddReferenceToCollection, ClearChanges, ResetResource and SetReference.
The reason is that in my example I don’t use them.

The Calling Method Example

    class Program

    {

        static void Main(string[] args)

        {

            // build the proxy

            var proxy = new CoursesDataContext(

                    new Uri("http://localhost:4205/CoursesService.svc/"));

            proxy.MergeOption = MergeOption.AppendOnly;

 

            var CGCourse = new Course

            {

                CourseID = 4,

                Creadit = 5,

                Days = 3,

                Title = "Computer Graphics"

            };

 

            proxy.AddToCourses(CGCourse);

            proxy.SaveChanges();

 

            PrintCoursesToConsule(proxy);

 

            CGCourse.Creadit = 3;

            proxy.UpdateObject(CGCourse);

            proxy.SaveChanges();

 

            PrintCoursesToConsule(proxy);

 

            proxy.DeleteObject(CGCourse);

            proxy.SaveChanges();

 

            PrintCoursesToConsule(proxy);

 

            Console.Read();

        }

 

        private static void PrintCoursesToConsule(CoursesDataContext proxy)

        {

            var courses = from course in proxy.Courses

                          select course;

 

            foreach (Course course in courses)

            {

                Console.WriteLine(

                    "Course name: {0}, Course duration: {1}, Course credit: {2}",

                    course.Title,

                    course.Days,

                    course.Creadit

                    );

            }

 

            Console.WriteLine();

        }

    }

Example Revealed

  • Create – when I want to add a new course I use the method AddToCourses of
    the proxy or AddObject method (which I need to supply the name of the
    resource which the object will the added to). After the call for one of these
    method, a call to SaveChanges method will create the object on the
    data service’s side. The following methods will be called on the data context in
    the order I use -  CreateResource, SetValue (for every property), SaveChanges
    and ResolveResource. In the CreateResource method I use the fullTypeName
    parameter to create the type with reflection, add it to the courses list and
    return it as the created object. The SetValue method will be called for every
    property of the object and set all them also using reflection. SaveChanges is an
    empty method which do nothing because we are using an in-memory object.
    ResolveResource return the object it get because we don’t need to resolve
    anything.
  • Update – when we want to update an existing object we call the UpdateObject
    method of the proxy class. After that call the call to SaveChanges will pass the
    request to the data context. The methods that will be called on the data context
    will be – GetResource, SetValue, SaveChanges and ResolveResource.
    The GetResource gets a IQueryable query which holds the data to update. I extract
    the resource to update and return it. The other methods where explained in the
    create bullet.
  • Delete – in order to the delete an object I use the DeleteObject method followed
    by the SaveChanges method. The chain of method that will be called on the data  
    context will be – GetResource, DeleteResource, SaveChanges and
    ResolveResource. The only method that concerns us (all the other where explain
    earlier) is the DeleteResource method that only removes the object it gets from
    the courses list.

Summary
Lets sum up the post, in order to use CRUD operations in a custom provider
of a data service you need to implement the IUpdatable interface. I provided
an example of how to implement a simple data context and I hope it will help
you to build your own. In the next post in the ADO.NET data services I’ll
write about how to perform batch operations.

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=""> <s> <strike> <strong>

*

2 comments

  1. Alan BOctober 19, 2008 ב 15:31

    Why microsoft did not build CRUD into ado.net data services i just don’t know.

    Well past time it worked out the box instead of us having to jump through hoops

    Reply
  2. Gil FinkOctober 20, 2008 ב 2:56

    Hello Alan B,
    I guess the main issue of not to build CRUD into ADO.Net data services is to enable flexibility. You have the IUpdatable interface and you can implement it as you wish. Furthermore, Entity Framework which Microsoft pushes as a mean to use inside the data services implements the interface out of the box and you don’t have to implement it there. In my example I show how to build a custom provider and therefore I have to implement the interface by myslef.

    Reply