Monday, 15 September 2014

Microsoft Web API 2 Application using EntityFramework 6.0 supporting oData v4




Web API 2 Application using EntityFramework 6.0 supporting oData v4

  • Using Microsoft Visual Studio Express 2013 for Web
  • Create new Project Alerts  of Template  WebAPI
  • The project is targeted for .NET Framework 4.5
  • Using NuGet Package Manager update/add all the latest packages as listed in the Packages.config shown below


<?xml version="1.0" encoding="utf-8"?>
    <packages>
      <package id="EntityFramework" version="6.0.0" targetFramework="net45" />
      <package id="Microsoft.AspNet.WebApi" version="5.2.2" targetFramework="net45" />
      <package id="Microsoft.AspNet.WebApi.Client" version="5.2.2" targetFramework="net45" />
      <package id="Microsoft.AspNet.WebApi.Core" version="5.2.2" targetFramework="net45" />
      <package id="Microsoft.AspNet.WebApi.OData" version="5.3.0" targetFramework="net45" />
      <package id="Microsoft.AspNet.WebApi.WebHost" version="5.2.2" targetFramework="net45" />
      <package id="Microsoft.Data.Edm" version="5.6.2" targetFramework="net45" />
      <package id="Microsoft.Data.OData" version="5.6.2" targetFramework="net45" />
      <package id="Microsoft.OData.Client" version="6.7.0" targetFramework="net45" />
      <package id="Microsoft.OData.Core" version="6.7.0" targetFramework="net45" />
      <package id="Microsoft.OData.Edm" version="6.7.0" targetFramework="net45" />
      <package id="Microsoft.Spatial" version="6.7.0" targetFramework="net45" />
      <package id="Newtonsoft.Json" version="6.0.5" targetFramework="net45" />
      <package id="System.Spatial" version="5.6.2" targetFramework="net45" />
    </packages>


    Create entity data model for the database


    • Add->New Item->Data->ADO.NET Entity Data Model
    • Name it as AlertsModel.edmx
    • Chose Model Contents -> Generate from database
    • In Next step , create connection string by selecting your database and select entity connection settings in Web.config with name as AlertsEntities
    • In Next step, select the tables and views you want to expose, in our example view called AlertsView1 and name Model namespace as AlertsModel

    Add Controller derived from oDataController

    This either we could create manually or use oData scaffolding

    Manual Creation:
    • Controllers->Add->Controller->Web API 2 Controller - Empty
    • Name the Controller as AlertsController
    • Modify the class to derive from ODataController
    • Add Get method as follows


    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Net.Http;
    using System.Web.Http;
    using System.Web.OData;

    namespace Alerts.Controllers
    {
        public class AlertsController : ODataController
        {
            AlertsEntities db = new AlertsEntities();
          
            protected override void Dispose(bool disposing)
            {
                db.Dispose();
                base.Dispose(disposing);
            }
            [EnableQuery]
            public IQueryable<AlertsView1> Get()
            {
             
                return db.AlertsView1;
            }
        }
    }

    Adding  a route for this controller

    • Edit WebApiConfig.cs file under App_Start folder
    • Remove the HttpRoute entries and add Odata route as follows

    using Microsoft.OData.Edm;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web.Http;
    using System.Web.OData.Builder;
    using System.Web.OData.Extensions;

    namespace Alerts
    {
        public static class WebApiConfig
        {
            public static void Register(HttpConfiguration config)
            {

                config.MapODataServiceRoute(
                    routeName: "ODataRoute",
                    routePrefix: null,
                    model: GenerateEdmModel());
            }
           

            private static IEdmModel GenerateEdmModel()
            {
                 var builder = new ODataConventionModelBuilder();
                 builder.EntitySet<Alerts.AlertsView1>("Alerts");
                 return builder.GetEdmModel();
            }
        }
    }

    The EntitySet is named as Alerts and EntityType is named as AlertsView1

    In Web.config under runtime section add BindingRedirect for the newer versions of dll as follows


     <runtime>
        <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">

          <dependentAssembly>    
            <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
            <bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
          </dependentAssembly>

        <dependentAssembly>
            <assemblyIdentity name="System.Net.Http.Formatting" publicKeyToken="31bf3856ad364e35" culture="neutral" />
            <bindingRedirect oldVersion="5.0.0.0" newVersion="5.2.2.0" />
          </dependentAssembly>

          <dependentAssembly>
            <assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35" culture="neutral" />
            <bindingRedirect oldVersion="5.0.0.0" newVersion="5.2.2.0" />
          </dependentAssembly>

          <dependentAssembly>
            <assemblyIdentity name="Microsoft.OData.Core" publicKeyToken="31bf3856ad364e35" culture="neutral" />
            <bindingRedirect oldVersion="0.0.0.0-6.7.0.0" newVersion="6.7.0.0" />
          </dependentAssembly>

          <dependentAssembly>
            <assemblyIdentity name="Microsoft.OData.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral" />
            <bindingRedirect oldVersion="0.0.0.0-6.7.0.0" newVersion="6.7.0.0" />
          </dependentAssembly>

          <dependentAssembly>
            <assemblyIdentity name="Microsoft.Spatial" publicKeyToken="31bf3856ad364e35" culture="neutral" />
            <bindingRedirect oldVersion="0.0.0.0-6.7.0.0" newVersion="6.7.0.0" />
          </dependentAssembly>

          <dependentAssembly>
            <assemblyIdentity name="System.Spatial" publicKeyToken="31bf3856ad364e35" culture="neutral" />
            <bindingRedirect oldVersion="5.6.0.0" newVersion="5.6.2.0" />
          </dependentAssembly>

          <dependentAssembly>
            <assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral" />
            <bindingRedirect oldVersion="5.6.0.0" newVersion="5.6.2.0" />
          </dependentAssembly>

          <dependentAssembly>
            <assemblyIdentity name="Microsoft.Data.oData" publicKeyToken="31bf3856ad364e35" culture="neutral" />
            <bindingRedirect oldVersion="5.6.0.0" newVersion="5.6.2.0" />
          </dependentAssembly>
          
        </assemblyBinding>
      </runtime>


    Build and run the application under IIS Express 
    URL : http://localhost:58403/

    output:
    {
      "@odata.context":"http://localhost:58403/$metadata","value":[
        {
          "name":"Alerts","kind":"EntitySet","url":"Alerts"
        }
      ]
    }

    URL: http://localhost:58403/$metadata


    <edmx:Edmx xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx" Version="4.0">

    <edmx:DataServices>

    <Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="Alerts">

    <EntityType Name="AlertsView1">

    <Key>

    <PropertyRef Name="id"/>
    </Key>

    <Property Name="id" Type="Edm.Int32" Nullable="false"/>

    <Property Name="AlertSubType" Type="Edm.String"/>

    <Property Name="AlertType" Type="Edm.String"/>

    <Property Name="LocationName" Type="Edm.String"/>
    <Property Name="alertMessage" Type="Edm.String"/>
    <Property Name="Payload" Type="Edm.String"/>

    </EntityType>
    </Schema>

    <Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="Default">

    <EntityContainer Name="Container">

    <EntitySet Name="Alerts" EntityType="Alerts.AlertsView1"/>
    </EntityContainer>
    </Schema>
    </edmx:DataServices>
    </edmx:Edmx>


    Next You may Deploy it under IIS

    Android App consuming oData v4 Service

    This Android Application demonstrates


    1. how to consume oData V4 Service
    2. Periodically pull the data using timer from external oData Service
    3. Show data in multi-column list view
    4. Show different column list on portrait and landscape mode

    oData is a industry standard protocol for consuming and producing data API built on HTTP and REST.
     Refer http://www.odata.org/ for details of oData.


    I am using http://olingo.apache.org/ Apache olingo android client library for odata v4 support



    http://odata4j.org/ does not support oData v4



     Apache olingo library's support for V4 is still under beta, one need to download the source from Apache olingo Source and build android library using Apache maven


    For setting up oData V4 Server application using Microsoft Visual Studio 2013 refer to my blog

     Web API 2 Application using EntityFramework 6.0 supporting oData v4

    This website is hosted in my PC with website name as Alerts

    Pre-requisites
    I have setup Android development environment . I am using Eclipse 
        Version: Juno Service Release 2
       Build id: 20130225-0426
    and Android SDK upto date

      The Project consists of mainly MainActivity with a ListView to display Alerts list, A Custom Adapter (AlertsAdapter class) to feed data to the ListView, and a DataService class which pulls data from Server using oData protocol and pump it to Adapter class.
    MainActivity uses a Timer task to pull data periodically from Server

    1. Create Project 

    Create Empty Android Project with MainActivity extending Activity
    Add a ListView to activity_main.xml layout


    2. Create Data Object class AlertsData


    3. Create DataService class

    Since DataService is going to use networking/Sockets underneath, we need to run it in non-UI thread, hence it is created as Async Task class , so extend this from AsyncTask<> class
    Override doInBackground() and onPostExecute() methods

    doInBackground

    In this method connect to oData V4 data service, get Alerts entity set and store it in an array of custom data object (AlertData)

    onPostExecute

    In this method AlertsData is filled into ListView adapter object. Every time old data is cleared before new data is added

    Since this class is going to use the oData classes copy the odata jar file (the version I built from source) 
    olingo-client-android-4.0.0-beta-01-SNAPSHOT.jar into project's libs folder and add as External Jar to buildpath

    Add following member variables to the class




    Fill  the methods doInBackground and onPostExecute as shown below




    4. Create Custom DataAdapter class


    Create the layout xml for columns for the ListView row

    alerts_listitem_row.xml



    The LinearLayout is set with orientation Horizontal and each column is a TextView
    The weights are adjusted according to column width requirement
    This layout will be used in AlertsAdapter class by inflating thsi for each row data

    Create AlertsAdapter derived from ArrayAdapter<AlertsData>
    create a nested class alertHolder to hold textviews for all the columns of ListView row 

    override getView() to custom display each row
    Add helper functions FillHolder() and displayFromHolder() to get TextViews for each of the columns and display values for each row column values




    5. Binding Together


    Following two member variables are added to class MainActivity and the below code in onCreate() method -




    6. Application Configuration Changes


    We force the ListView display to landscape orientation by setting in AndroidManifest.xml Activity attribute as shown -

    ...
    <activity
            android:name=".MainActivity"

    android:screenOrientation="landscape"
    ...

    Since we need to access network, INTERNET Access permission need to be requested in  in AndroidManifest.xml
    ...
    <uses-permission android:name="android.permission.INTERNET"/>
    ...

    7. Build & Run the Application

    Application was run under these two Devices:

    iBall Slide Phablet model 7236 2G dual core cortex A7 1.3 ghz
    Android 4.2.2 SDK 17

    Xolo X900 Intel Atom duel core 1.6 GHZ
    Android 4.0.4 SDK 15

    Screen snapshot on Xolo X900



    You may download the project from AlertsAppV1.zip or
    github AlertsAppv1

    8. Using Timer to periodically pull data



    Create an TimerTask extended class



    Modify MainActivity class onCreate() method as follows


    The dataService is created and executed in TimerTask instead of in onCreate()

    Please note we need to create each time new instance of DataService, as one instance can execute only once


    Build & Run

    Application ran on iBall Slide as expected, executing getData every 10 sec

    However on Xolo, throws 


    java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()


    It looks like problem is with older version of android (SDK 15) and seem to work on SDK 17

    Anyway ensuring DataService task runs in UI thread, the problem gets solved as follows -


    You may download this version from  AlertsAppV2.zip

    9. Enhancing the application to show different column list on portrait and landscape mode

    Need to do following changes

    • create a new list item row for portrait mode
    • create a new AlertsAdapter class to handle data display in ListView 
    • Override onConfigurationChanged to intercept orientation changes



    Modify the Manifest  file as follows -

    • remove under activity the orientation   android:screenOrientation="landscape"
    • add  android:configChanges="orientation|keyboardHidden|screenSize"




    alerts_listitem_min_row.xml



    Modify AlertsAdapter class qualifying AlertsHolder class as protected
    Create a class AlertsAdapterMini extending AlertsAdapter class and override FillHolder and displayFromHolder methods as follows -





    override onConfigurationChanged to intercept orientation change in MainActivity class -



    Next Modify the onCreate method of MainActivity class



    Build and run the project

    Screenshot when Landscape mode




    Screenshot when Portrait mode





    You may download this version from AlertsAppV3.zip