Showing posts with label ODATA. Show all posts
Showing posts with label ODATA. Show all posts

Wednesday, November 4, 2015

OData v4 Endpoint Using ASP.NET Web API 2.2 & Visual Studio 2015

In this tutorial, we will do the following:
  1. Create an empty web application in Visual Studio.
  2. Add a Student model and use Code First Entity Framework to create the database and seed it with sample data.
  3. Create an OData v4 controller, which will act as the OData service endpoint.
  4. Create a separate client console application that will access the OData service.
Let’s get started.

Creating the Server OData Student Service

We will first create a new web project in Visual Studio 2015.
File >> New > Project >> Installed >> Templates >> Visual C# >> Web
Select the ASP.NET Web Application template. Name the project "StudentService".

image

Visual Studio Extensions

We need to add two extensions that pertain the V4 of the OData standard, namely:
  1. OData v4 Web API Scaffolding
  2. OData v4 Client Code Generator
To install these extensions:
  • Tools >> Extensions and Updates…
  • Enter “odata” in the search box and install these two extensions:
image

Add a Model Class

In Solution Explorer, right-click the Models folder. From the context menu, select Add >> Class. Name the class Student. In the Student.cs file, replace the class code with the following:
public class Student {
  public int StudentId { get; set; }
  public string FirstName { get; set; }
  public string LastName { get; set; }
  public string Major { get; set; }
}
Entity Framework Code First
Install the NuGet package for EF. From the Tools menu, select NuGet Package Manager > Package Manager Console. In the Package Manager Console window, type:
Install-Package EntityFramework
In the Web.config file, add the following section after the closing </configSections> tag:
<connectionStrings>
   <add name="StudentDB" connectionString="Data Source=(localdb)\v11.0; 
   Initial Catalog=StudentDB; Integrated Security=True; MultipleActiveResultSets=True; 
   AttachDbFilename=|DataDirectory|StudentDB.mdf" 
   providerName="System.Data.SqlClient" />
</connectionStrings>
Note: If you are using localdb version 12, then the connection string data source would be: 
Data Source=(localdb)\mssqllocaldb.
Next, add a class named StudentContext to the Models folder:
public class StudentContext : DbContext {
   public StudentContext() : base("name=StudentDB") { }
   public DbSet<Student> Students { get; set; }
}

Migrations

1) To enable migrations run the following command in the Package Manager Console:
enable-migrations -ContextTypeName StudentContext -MigrationsDirectory Migrations\StudentMigrations
2) Open the Configuration.cs file in the /Migrations/StudentMigrations folder. Replace the Seed() method with the following code:
protected override void Seed(StudentContext context) {
  context.Students.AddOrUpdate(
  s => new { s.FirstName, s.LastName },
  new Student { FirstName = "Andrew", LastName = "Peters", Major = "Pharmacy" },
  new Student { FirstName = "Brice", LastName = "Lambson", Major = "Business" },
  new Student { FirstName = "Rowan", LastName = "Miller", Major = "Medicine" },
  new Student { FirstName = "Tom", LastName = "Doe", Major = "Engineering" },
  new Student { FirstName = "Bob", LastName = "Fox", Major = "City Planning" },
  new Student { FirstName = "Sue", LastName = "Ace", Major = "Forestry" },
  new Student { FirstName = "Joe", LastName = "Gad", Major = "Mining" },
  new Student { FirstName = "Sam", LastName = "Roy", Major = "Energy" }
  );
  context.SaveChanges();
}
3) Add a migration by running the following command in the Package Manager Console:
add-migration -ConfigurationTypeName StudentService.Migrations.StudentMigrations.Configuration "InitialCreate"
4) Next, we will create and seed the database by running this command in the Package Manager Console:
update-database -ConfigurationTypeName StudentService.Migrations.StudentMigrations.Configuration

Create OData Controller:

To the Controllers folder, add a controller and select “Microsoft OData V4 Web API Controller using Entity Framework”

image

Make the following choices on the “Add Controller” wizard:

image

Click on the Add button. The controller gets created.
Open the StudentsController. You will find this information in the class comment:
The WebApiConfig class requires additional changes. Merge these statements into the Register method of the WebApiConfig class as applicable. Note that OData URLs are case sensitive.
using System.Web.OData.Builder;
using System.Web.OData.Extensions;
using StudentService.Models;
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Student>("Students");
config.MapODataServiceRoute("odata", "odata", builder.GetEdmModel());

Run the server-side application

Hit Ctrl-F5 to run the web application in a browser at address /odata/Students. This should display:
Note: The URL is case-sensitive with OData.
{
  "@odata.context":"http://localhost:51103/odata/$metadata#Students","value":[
    {
      "StudentId":1,"FirstName":"Andrew","LastName":"Peters","Major":"Pharmacy"
    },{
      "StudentId":2,"FirstName":"Brice","LastName":"Lambson","Major":"Business"
    },{
      "StudentId":3,"FirstName":"Rowan","LastName":"Miller","Major":"Medicine"
    },{
      "StudentId":4,"FirstName":"Tom","LastName":"Doe","Major":"Engineering"
    },{
      "StudentId":5,"FirstName":"Bob","LastName":"Fox","Major":"City Planning"
    },{
      "StudentId":6,"FirstName":"Sue","LastName":"Ace","Major":"Forestry"
    },{
      "StudentId":7,"FirstName":"Joe","LastName":"Gad","Major":"Mining"
    },{
      "StudentId":8,"FirstName":"Sam","LastName":"Roy","Major":"Energy"
    }
  ]
}
Try out these additional endpoints after adjusting the port number to suit your environment:
/odata
/odata/$metadata
/odata/Students(3)

Create an OData v4 Client Console App


Keep the server application running. Start a new instance of Visual Studio and create an independent Console Application.

File >> Add > Project >> Installed >> Visual C# >> Console Application


Name the project StudentsClientApp.

Generate the Service Proxy

Right-click the StudentsClientApp console project. Select Add >> New Item >> Visual C# Items >> Code >> OData Client. 
Name the template "StudentClient.tt". 

image

Open the StudentClient.tt file. Set the value of MetadataDocumentUri to the metadata URL of your service. In the case of my example this would be:
public const string MetadataDocumentUri = http://localhost:58005/odata/$metadata;
Note: You must adjust the port number to match your environment.
Note: Needless to say, you need to change the port number to suit your environment.
As soon as you save StudentClient.tt, the proxy class will be created in the StudentClient.cs file. If this is not the case, then right-click on it and choose “Run Custom Tool”.

image

Now that we have a proxy, we can write the code that accesses the service.
Replace the Program class with the following code:

class Program {
    // Get an entire entity set.
    static void ListAllStudents(Default.Container container) {
        foreach (var s in container.Students) {
            Console.WriteLine("{0}, {1}, {2}", s.FirstName, s.LastName, s.Major);
        }
        Console.WriteLine(string.Concat(Enumerable.Repeat("=", 50)));
    }

    static void AddStudent(Default.Container container, Student student) {
        container.AddToStudents(student);
        var serviceResponse = container.SaveChanges();
        foreach (var operationResponse in serviceResponse) {
            Console.WriteLine("Response: {0}\n", operationResponse.StatusCode);
        }
    }

    static void Main(string[] args) {
        // Adjust the following port number to suit your environment.        string serviceUri = "http://localhost:51850/odata/";
        var container = new Default.Container(new Uri(serviceUri));
        ListAllStudents(container);
        int count = container.Students.Count();
        var student = new Student() {
            FirstName = "First " + (count + 1),
            LastName = "Last " + (count + 1),
            Major = "Major " + (count + 1)
        };

        AddStudent(container, student);
        ListAllStudents(container);
        Console.ReadKey();
    }
}


After making sure the Server application is running, run the Client application by right-clicking on the console application and choosing: Debug >> Start new instance

image

You should see the following console window:

image

Note that every time you run the client a new student gets added. The output above shows the list of students before and after a record is added.
I trust this walk-through is useful to you.






Monday, August 27, 2012

Uploading documents into SharePoint 2010 using OData

I experienced some pain when asked to develop a utility to upload documents into SharePoint 2010 using OData. Therefore, I naturally decided on this post in order to save others the pain I went through. This post explains how to do it using a simple client-side C# command-line application:

Step 1:

Create a C# command-line application in Visual Studio 2010 on the same box as your SharePoint 2010 server. Add a proxy to the SharePoint 2010 OData service on your server. This is done by right-clicking on your project “References” node and choosing “Add Service Reference …”.

image

The service that needs to be consumed is listdata.svc. Enter a URL similar to http://myserver/_vti_bin/listdata.svc then click on the “Go” button. Once the service is found, give the service NameSpace the name ListdataServiceReference.

Step 2:

I placed a text file named bogus.txt in the root of my c: drive with some arbitrary content. I also created a document library named BogusDocumentLibrary in the root site of my SharePoint server. Here is the upload() method code for uploading file c:\bogus.txt into the document library named BogusDocumentLibrary.

private static void upload() {
    string sharePointSvc = "
http://myserver/_vti_bin/listdata.svc";

    using (FileStream file = File.Open(@"c:\bogus.txt", FileMode.Open)) {
        ListdataServiceReference.HomeDataContext ctx
            = new ListdataServiceReference.HomeDataContext(new Uri(sharePointSvc));

       // ctx.Credentials = System.Net.CredentialCache.DefaultCredentials;

        string username = "alice";
        string password = "wonderland";
        string domain = "myserver";

        ctx.Credentials = new System.Net.NetworkCredential(username, password, domain);

        string path = "/BogusDocumentLibrary/Bogus.txt";
        string contentType = "plain/text";
        ListdataServiceReference.BogusDocumentLibraryItem documentItem = new ListdataServiceReference.BogusDocumentLibraryItem()
        {
            ContentType = contentType,
            Name = "Bogus",
            Path = path,
            Title = "Bogus"
        };

        ctx.AddToBogusDocumentLibrary(documentItem);

        ctx.SetSaveStream(documentItem, file, false, contentType, path);

        ctx.SaveChanges();
    }
}

Needless to say, you must resolve the missing System.IO namespace.

Context
The name of the proxy class representing your context is named depending on your site’s name. In my case, my site is named home so the proxy context class is HomeDataContext.

Credentials
If you are accessing the service on the same box or domain as the server then it suffices that you pass on the default credentials with “System.Net.CredentialCache.DefaultCredentials”. Otherwise, if you are on a different domain then you should use the technique described in the code above where the username, password, and domain with access rights to the SharePoint site are passed on to the server.

Document Properties
The item proxy class name that represents the document library depends on the name given to it. In my case, I named the document library BogusDocumentLibrary so the item proxy class name is BogusDocumentLibraryItem. Once an instance of this class in instantiated then the various properties such as ContentType, Name, Path, and Title must be set.

Upload document
Finally, these three lines are responsible for uploading the document:

ctx.AddToBogusDocumentLibrary(documentItem);
ctx.SetSaveStream(documentItem, file, false, contentType, path);
ctx.SaveChanges();

Finally, you can call the upload() method from within your main method and it should all work as long as the appropriate server, text file, and document library exist.

Tuesday, July 3, 2012

Northwind-mania: Simple example on working with Northwind oData using KnockoutJS

In my previous post I demonstrated how one can easily render data on the client side from the Categories table in the Northwind database using KnockoutJS. This post is a slight variation of my previous post whereby the source of the data is an oData feed originating from http://services.odata.org/Northwind/Northwind.svc.

Pre-requisites:

  • Visual Studio 2010
  • MVC 3.0
  • NuGet
  • KnockoutJS
  • DataJS

Step 1:

Start a new New Web Site project in Visual Studio 2010 using the ASP.NET Empty Web Site template.

Step 2:

In Solution Explorer, right-click on the top node and select “Manage NuGet Packages …”.

image

Step 3:

Enter “knockout” in the search field on the top right-hand-side of the next screen. Select “knockoutjs” then click on the “Install” button.

image

Step 4:

In addition to knockoutjs, we will be using other JavaScript libraries jQuery and DataJS. DataJS facilitates access to oData. Therefore, repeat Step-3 for jQuery and DataJs libraries.

image

image

Ensure that the jQuery, DataJs and KnockoutJs libraries are installed into your project under the Scripts folder as shown below:

image

Step 5:

Add a new page named “Default.htm” to your website.

Open the Default.htm file. Drag and drop the JavaScript files jquery-1.7.2.js, knockout-2.1.0.js and datajs-1.0.3.js from the Scripts folder into the Default.htm file just below the ending </title> tag. This produces the <script> tag pointing to the respective JavaScript files. The page should resemble the following:

<!DOCTYPE>
<html>
<head>
    <title></title>
    <script src="Scripts/jquery-1.7.2.js" type="text/javascript"></script>
    <script src="Scripts/knockout-2.1.0.js" type="text/javascript"></script>
    <script src="Scripts/datajs-1.0.3.js" type="text/javascript"></script>
</head>
<body>

</body>
</html>

 

Step 6:

Place the following HTML into the body section of the Default.htm file:

<ul data-bind="foreach: categories">    
     <li><span data-bind="text: $data.CategoryName"></span></li>
</ul>
<script type="text/javascript">
     var ServiceURL = "
http://services.odata.org/Northwind/Northwind.svc";

     OData.read(ServiceURL + "/Categories?$format=json", CallbackFunction);

     function CallbackFunction(data, request) {
         var viewModel = {
             categories: data.results
         };
         ko.applyBindings(viewModel);
     }
</script>

Let’s analyze the above code. The most important line is:

OData.read(url, CallbackFunction);

The OData.read() function comes from the “DataJS” library. Its first argument is an oData URL, and the second argument is a callback function that is called once a response is received from the server. Notice that the oData URL contains “$format=json” so that data returned from the service is in JSON format.

The callback function reads the JSON data encapsulated in property data.results into the categories property of the KnockoutJS viewModel. Finally, the viewModel is bound to the view with the function call ko.applyBindings(viewModel).

On the view side, data is rendered to the web page as follows:

<ul data-bind="foreach: categories">
<li><span data-bind="text: $data.CategoryName"></span></li>
</ul>

The final state of the web page is:

<!DOCTYPE>
<html>
<head>
    <title></title>
    <script src="Scripts/jquery-1.7.2.js" type="text/javascript"></script>
    <script src="Scripts/knockout-2.1.0.js" type="text/javascript"></script>
    <script src="Scripts/datajs-1.0.3.js" type="text/javascript"></script>
</head>
<body>
    <ul data-bind="foreach: categories">
        <li><span data-bind="text: $data.CategoryName"></span></li>
    </ul>
    <script type="text/javascript">
        var ServiceURL = "
http://services.odata.org/Northwind/Northwind.svc";

        OData.read(ServiceURL + "/Categories?$format=json", CallbackFunction);

        function CallbackFunction(data, request) {
            var viewModel = {
                categories: data.results
            };
            ko.applyBindings(viewModel);
        }
    </script>
</body>
</html>

This example, of course, a very simplistic. It does, however, illustrate how the various pieces fit together involving KnockoutJS and oData.

Hit F5 in Visual Studio 2010.

You may receive a browser warning because a request is being made to another server at http://www.odata.org. I received the following dialog, and clicked on the “Yes” button:

image

The output is identical to my previous post. The difference, this time, is that we are using plain HTML and all data access is being done using client-side technologies.

image