Search code examples
wcf-data-servicesodata

oData with custom providers


I have an exising server framework (WCF services), that I need to upgrade to "oData" format. I downloaded some oData samples from the "oData" site: http://www.odata.org/ecosystem

I want to avoid using "Entity Framework" because it will require rewriting alot of my code, so I settled for "Custom Provider" solution. Something strikes me wierd in all the examples I see:

The Invocation of the WCF method calls "CreateDataSource()" method (code attached), that initializes the entire data model from scratch, for every invocation. In this demo version it is not a big deal to reload 6 instances of bussiness entities, and then filter them, but in real life : I would have hundereds of tables, containing 1000-1000000 rows each.

How should I avoid loading my entire DB to memory each time?? I am probably missing something, otherwise this technology would not have been useful.

 protected override DSPContext CreateDataSource()
    {
        DSPContext context = new DSPContext();

        ResourceSet productsSet, categoriesSet;
        this.Metadata.TryResolveResourceSet("Products", out productsSet);
        this.Metadata.TryResolveResourceSet("Categories", out categoriesSet);
        IList<DSPResource> products = context.GetResourceSetEntities(productsSet.Name);
        IList<DSPResource> categories = context.GetResourceSetEntities(categoriesSet.Name);

        var categoryFood = new DSPResource(categoriesSet.ResourceType);
        categoryFood.SetValue("ID", 0);
        categoryFood.SetValue("Name", "Food");
        categoryFood.SetValue("Products", new List<DSPResource>());
        categories.Add(categoryFood);

        var categoryBeverages = new DSPResource(categoriesSet.ResourceType);
        categoryBeverages.SetValue("ID", 1);
        categoryBeverages.SetValue("Name", "Beverages");
        categoryBeverages.SetValue("Products", new List<DSPResource>());
        categories.Add(categoryBeverages);

        var categoryElectronics = new DSPResource(categoriesSet.ResourceType);
        categoryElectronics.SetValue("ID", 2);
        categoryElectronics.SetValue("Name", "Electronics");
        categoryElectronics.SetValue("Products", new List<DSPResource>());
        categories.Add(categoryElectronics);

        var productBread = new DSPResource(productsSet.ResourceType);
        productBread.SetValue("ID", 0);
        productBread.SetValue("Name", "Bread");
        productBread.SetValue("Description", "Whole grain bread");
        productBread.SetValue("ReleaseDate", new DateTime(1992, 1, 1));
        productBread.SetValue("DiscontinueDate", null);
        productBread.SetValue("Rating", 4);
        productBread.SetValue("Category", categoryFood);
        productBread.SetValue("BackupCategoryID", 2);
        productBread.SetValue("RelatedProductID", 1);
        products.Add(productBread);

        var productMilk = new DSPResource(productsSet.ResourceType);
        productMilk.SetValue("ID", 1);
        productMilk.SetValue("Name", "Milk");
        productMilk.SetValue("Description", "Low fat milk");
        productMilk.SetValue("ReleaseDate", new DateTime(1995, 10, 21));
        productMilk.SetValue("DiscontinueDate", null);
        productMilk.SetValue("Rating", 3);
        productMilk.SetValue("Category", categoryBeverages);
        productMilk.SetValue("BackupCategoryID", 2);
        productMilk.SetValue("RelatedProductID", 2);
        products.Add(productMilk);

        var productWine = new DSPResource(productsSet.ResourceType);
        productWine.SetValue("ID", 2);
        productWine.SetValue("Name", "Wine");
        productWine.SetValue("Description", "Red wine, year 2003");
        productWine.SetValue("ReleaseDate", new DateTime(2003, 11, 24));
        productWine.SetValue("DiscontinueDate", new DateTime(2008, 3, 1));
        productWine.SetValue("Rating", 5);
        productWine.SetValue("Category", categoryBeverages);
        productWine.SetValue("BackupCategoryID", 4);
        productWine.SetValue("RelatedProductID", 3);
        products.Add(productWine);

        ((List<DSPResource>)categoryFood.GetValue("Products")).Add(productBread);
        ((List<DSPResource>)categoryBeverages.GetValue("Products")).Add(productMilk);
        ((List<DSPResource>)categoryBeverages.GetValue("Products")).Add(productWine);

        return context;

Solution

  • These are just samples. A real world solution would store the data in some "Store" and have an implementation of IQueryable over that store. Since that implementation is usually a lot of code and not directly related to the sample intent, it was not included in the samples.

    So to answer your question:

    • You need an IQueryable implementation over the store you use. If this is a DB, then you need some ORM (like EF) to map the data base to CLR space. That ORM usually also provides IQueryable implementation. Other than EF, there's also NHibernate or LINQ to SQL.

    • You need to write the custom provider code (implementing interfaces like IDataServiceMetadataProvider, IDataServiceQueryProvider and so on) as shown in the sample, and return the store's IQueryable from the GetQueryRootForResourceSet (where the sample uses LINQ to Object's IQueryable implementation).

    Note that custom provider is a lot of work. If you can use an ORM like NHibernate or LINQ to SQL, using the reflection provider instead would be much quicker and might still work.