Wednesday, November 10, 2010

WCF Data Services with Custom Data Source and Reflection Data Service Provider

I am going to show an example how to expose custom data source using wcf data services and OData. This is just a simple test. first we need to add a new WCF Data Service to our website. //ODataCustomService.svc
<%@ ServiceHost Language="C#"
Factory="System.Data.Services.DataServiceHostFactory" Service="MyWcfServiceLibrary.ODataCustomService" %>
then we creat the service,

using System;
using System.Collections.Generic;
using System.Data.Services;
using System.Data.Services.Providers;
using System.Data.Services.Common;
using System.Linq;
using System.Web;
using System.ServiceModel.Web;
 
namespace MyWcfServiceLibrary
{
    [System.ServiceModel.ServiceBehavior(IncludeExceptionDetailInFaults = true)]
    [WcfDataServices.DataServicesJSONP.JSONPSupportBehavior]
    public class ODataCustomService : DataService<MyCustomDataSource>
    {
        public static void InitializeService(DataServiceConfiguration config)
        {
            config.SetEntitySetAccessRule("*", EntitySetRights.All);
            config.SetServiceOperationAccessRule("GetCarsByName", ServiceOperationRights.All);
        }
 
        protected override MyCustomDataSource CreateDataSource()
        {
            MyCustomDataSource source = new MyCustomDataSource();
            return source;
        }
 
        [WebGet]
        public List<Car> GetCarsByName(string name)
        {
            MyCustomDataSource context = this.CurrentDataSource;
            var q = from c in context.Cars where c.CarName == name select c;
            return q.ToList<Car>();
        }
 
    }
 
 
    public class MyCustomDataSource : IUpdatable
    {
        private static List<Car> _cars = new List<Car>();
 
        public IQueryable<Car> Cars { get { return _cars.AsQueryable(); } }
 
        static MyCustomDataSource()
        {
            _cars.Add(new Car() { CarID = 1, CarName = "ford" });
            _cars.Add(new Car() { CarID = 2, CarName = "bmw" });
            _cars.Add(new Car() { CarID = 3, CarName = "mazda" });
 
        }
 
        #region IUpdatable Members
 
        public void ClearChanges()
        {
            throw new NotImplementedException();
        }
 
        public object CreateResource(string containerName, string fullTypeName)
        {
            Type t = Type.GetType(fullTypeName);
            if (t == typeof(Car))
            {
                Car c = new Car();
                _cars.Add(c);
                return c;
            }
            else
                throw new Exception("type not found!");
        }
 
        public void DeleteResource(object targetResource)
        {
            if (targetResource.GetType() == typeof(Car))
            {
                _cars.Remove(targetResource as Car);
            }
            else
                throw new Exception("type not found!");
        }
 
        public object GetResource(IQueryable query, string fullTypeName)
        {
            if (query is IQueryable<Car>)
            {
                return ((IQueryable<Car>)query).FirstOrDefault<Car>();
            }
 
            throw new Exception("type not found!");
        }
 
        public object GetValue(object targetResource, string propertyName)
        {
            System.Reflection.PropertyInfo property = targetResource.GetType().GetProperty(propertyName);
            return property.GetValue(targetResource, null);
        }
 
        public void SetValue(object targetResource, string propertyName, object propertyValue)
        {
            System.Reflection.PropertyInfo property = targetResource.GetType().GetProperty(propertyName);
            property.SetValue(targetResource, propertyValue, null);
        }
 
        public object ResetResource(object resource)
        {
            return resource;
        }
 
        public object ResolveResource(object resource)
        {
            return resource;
        }
 
        public void SaveChanges()
        {
 
        }
 
        public void SetReference(object targetResource, string propertyName, object propertyValue)
        {
            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
    }
 
    [DataServiceKey("CarID")]
    public class Car
    {
        public int CarID { get; set; }
        public string CarName { get; set; }
    }
 
}


Then, we can call the service
http://localhost:222/MyWebSite/odata/ODataCustomService.svc/Cars
http://localhost:222/MyWebSite/odata/ODataCustomService.svc/Cars(1)
http://localhost:222/MyWebSite/odata/ODataCustomService.svc/Cars?$select=CarName
http://localhost:222/MyWebSite/odata/ODataCustomService.svc/Cars?$filter=CarName eq 'ford'
http://localhost:222/MyWebSite/odata/ODataCustomService.svc/Cars?$orderby=CardName
http://localhost:222/MyWebSite/odata/ODataCustomService.svc/GetCarsByName?name='ford'

and to test insert, update and delete I used Fiddler to test.

------------------------------------------------------------------------------------
INSERT
 
method : Post
content-type: application/atom+xml
 
http://localhost:57599/MyWebSite/odata/ODataCustomService.svc/Cars
Request Body:
 
<?xml version="1.0" encoding="utf-8" standalone="yes" ?> 
<entry xml:base="http://localhost:57599/MyWebSite/odata/custom/ODataCustomService.svc/"

xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices"
xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"
xmlns="http://www.w3.org/2005/Atom">
  <id>http://localhost:57599/MyWebSite/odata/custom/ODataCustomService.svc/Cars(1)</id> 
  <title type="text" /> 
  <updated>2010-11-08T15:21:15Z</updated> 
<author>
  <name /> 
  </author>
  <link rel="edit" title="Car" href="Cars(4)" /> 
  <category term="MyWcfServiceLibrary.Car" 
scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
<content type="application/xml">
<m:properties>
  <d:CarID m:type="Edm.Int32">4</d:CarID> 
  <d:CarName>benz</d:CarName> 
  </m:properties>
  </content>
  </entry>
 ---------------------------------------------------------------------------------
 DELETE  
  
 method: DELETE
 content-type: application/atom+xml
  
 http://localhost:57599/MyWebSite/odata/ODataCustomService.svc/Cars(1)
  
 ------------------------------------------------------------------------------------
 UPDATE
  
 method : PUT
 content-type: application/atom+xml
  
http://localhost:57599/MyWebSite/odata/ODataCustomService.svc/Cars(1)
Request Body:
 
<?xml version="1.0" encoding="utf-8" standalone="yes" ?> 
<entry xml:base="http://localhost:57599/MyWebSite/odata/custom/ODataCustomService.svc/" 
xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices"
xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns="http://www.w3.org/2005/Atom">
  <id>http://localhost:57599/MyWebSite/odata/custom/ODataCustomService.svc/Cars(1)</id> 
  <title type="text" /> 
  <updated>2010-11-08T15:21:15Z</updated> 
<author>
  <name /> 
  </author>
  <link rel="edit" title="Car" href="Cars(1)" /> 
  <category term="MyWcfServiceLibrary.Car" 
scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
<content type="application/xml">
<m:properties>
  <d:CarID m:type="Edm.Int32">1</d:CarID> 
  <d:CarName>updated</d:CarName> 
  </m:properties>
  </content>
  </entry>


Cheers!

2 comments:

lauren said...

Good and simple example on how to expose custom data source using wcf data services and OData.code is self explanatory too.But one question why the function public void SaveChanges() is left empty what purpose does it serve
digital certificate

Mahesh said...

Hi Saeed, thanks for posting this. How do you generate the Request header and body to test various operations of the service?

Thanks,
Mahesh

Post a Comment