To my opinion the day the Astoria incubation project was unveiled,  all about querying and pushing data through a webservice changed for good. I was pretty amazed to see that the REST paradigm was so easily embraced in the .NET world. Now some time later, this is an official release and it is called ADO.NET Data Services. For all of you that are not familiar with ADO.NET Data Services you can find more information here.

To put it shortly: <quote>“The goal of the ADO.Net Data Services framework is to facilitate the creation of flexible data services that are naturally integrated with the web, using URIs to point to pieces of data and simple, well-known formats to represent that data, such as JSON and plain XML.”</quote>.

In other words it gives a lightweight, though robust HTTP communication layer above the data sources, that serves remote parts of the application, for example a SilverLight or ASP.NET UI front-ends.

Natively ADS supports only EntityFramework, but they do provide also LINQ support. Having LINQ in the game means that we could query almost any data provider underneath – including Telerik Open Access ORM tool. Yes, that’s right because we support LINQ and IQueryable we are able to provide ADS support as well.

 

To start we have to create a web site first that will host our data service. This is pretty much straight forward as you probably know:

image

After creating the site we add a ADO.NET Data Service item named OADataService.svc:

image

Note: If you have installed .NET SP1 you should have the ADO.NET Data Service template in your “Add New Item” dialog.

 

After that you will end up with a code file inside your App_Code folder like this:

public class WebDataService : DataService< /* TODO: put your data source class name here */ >
{
    // This method is called only once to initialize service-wide policies.
    public static void InitializeService(IDataServiceConfiguration config)
    {
        // TODO: set rules to indicate which entity sets and service operations are visible, updatable, etc.
        // Examples:
        // config.SetEntitySetAccessRule("MyEntityset", EntitySetRights.AllRead);
        // config.SetServiceOperationAccessRule("MyServiceOperation", ServiceOperationRights.All);
    }
}

 

We will modify the file contents (it is not compilable even at the beginning), but first we have to build our data model.

To do so add a library project to the solution. The library project will host our data model and that way we will have a better separation of concerns.

image

 

After we have the library project in the solution we have to build our model using the Open Access tool. For the purpose of this example we will add only the Products table inside our model.

To do so first enable the project to use Open Access. You can do so by starting the wizard from two places in Visual Studio:

1. Main menu > OpenAccess > “Enable Project to use ORM…”

2. Invoking the context menu on the library project (named Model for the purpose of this example), then go to OpenAccess > Enable Project

The wizard will guide you through few steps and we are ready to roll:

image

After the project gets “Open Access enhanced” we can start the Reverse Mapping wizard (Main Menu > OpenAccess > Reverse Mapping (Table to Classes)) and have the Products entities generated:

image

The wizard will guide you through the whole procedure and eventually will generate the Product entity file that looks like:

public partial class Product
{
    int productID;
    //The 'no-args' constructor required by OpenAccess. 
    public Product()
    {
    }

    [Telerik.OpenAccess.FieldAlias("productID")]
    public int ProductID
    {
        get { return productID; }
        set { this.productID = value; }
    }
.
.
.
}

After we generated the entities we have to provide a data context implementation for our OADataService data service. Add a class named OADataContext.cs in your Model project.

What do we need inside the class to provide all resources needed for the data service? Actually the ADS team allowed us to have quite broad implementation of the class: What we really need are properties that implement the IQueryable interface thus exposing the underlying data in a standard way to the ADS communications stack above. So for the Products entities we put a property like:

public IQueryable<Product> Products
{
    get
    {
        return this.scope.Extent<Product>();
    }
}      

 

The scope variable is actually an instance of IObjectScope – Open Access implements the actual data context through this interface. So to start providing data we need a couple of lines of code and they look like:

/// <summary>
/// Summary description for OADataContext
/// </summary>
public partial class OADataContext : IDisposable
{
    // Fields
    IObjectScope scope = null;

    public OADataContext()
    {
        this.scope = this.ProvideScope();
    }

    public OADataContext(IObjectScope scope)
    {
        this.scope = scope;
    }

    public virtual IObjectScope Scope
    {
        get
        {
            if (this.scope == null)
            {
                // Attempt to get the Scope
                this.scope = this.ProvideScope();
            }
            return this.scope;
        }
    }

    public IQueryable<Product> Products
    {
        get
        {
            return this.scope.Extent<Product>();
        }
    }

    protected virtual IObjectScope ProvideScope()
    {
        IObjectScope scope = ObjectScopeProvider1.GetNewObjectScope();
        scope.TransactionProperties.AutomaticBegin = true;
        return scope;
    }
    #region IDisposable Members

    public void Dispose()
    {
        if (scope != null)
        {
            scope.Dispose();
            scope = null;
        }
    }
    #endregion
}

 

Then we have to update the file we generated for our OADataService (it generates with compilation errors, and I believe they did it on purpose :) )

Having a data context means that we will modify the OADataService file like this:

[System.ServiceModel.ServiceBehavior(IncludeExceptionDetailInFaults = true)]
public class OADataService : DataService<OADataContext>
{
    protected override void HandleException(HandleExceptionArgs args)
    {
        base.HandleException(args);
    }

    // This method is called only once to initialize service-wide policies.
    //We overide it to enable all CRUD operations by setting EntitySetRights.All
    public static void InitializeService(IDataServiceConfiguration config)
    {
        config.SetEntitySetAccessRule("*", EntitySetRights.All);
    }
}

 

You should note the following:

- You have to refer the Model project from the web site or you will not be able to access the OADataContext type.

- The WCF attribute [System.ServiceModel.ServiceBehavior(IncludeExceptionDetailInFaults = true)] is very helpful when troubleshooting the service – without it you will not be able to gather enough information about the errors that may arise.

- the OADataContext source code is already distributed with the product installer as of version 2008.3 1205.

 

In the next post I will guide you how not only to feed data to the client, but also to implement all CRUD operations on the server based on Open Access O/R tool.

Enjoy!


About the Author

Dimitar Kapitanov

Dimitar Kapitanov is Team Lead in Telerik Platform Team

Comments

Comments are disabled in preview mode.