JavaScript, WCF and Entity Framework Data Model for SharePoint 2013 Apps – Part 2

Home » SharePoint Development » JavaScript, WCF and Entity Framework Data Model for SharePoint 2013 Apps – Part 2

JavaScript, WCF and Entity Framework Data Model for SharePoint 2013 Apps – Part 2

Posted on

The following article is the second in a three part series on data model patterns for SharePoint 2013 apps.

In Part One of this series I set out the basic parameters for a data model pattern that can be used for building SharePoint 2013 apps and defined the process for creating a base data layer using the ADO.NET Entity Framework. In this post we will explore the second part of the pattern – building a middle-tier service layer with WCF.

Overview

The Entity Framework is a great addition to Visual Studio that increases developer efficiency by automatically generating data access classes, objects and functions. On its own, however, it isn’t of much use as part of a de-coupled, client-side programming model. While classic server-side applications can directly access the various entities and objects in the framework, a rich client app that uses HTML and JavaScript to deliver a modern, response UI without postbacks doesn’t have the same capabilities. The same can be said for mobile platform-dependent apps like those for IOS and Android. This necessitates the creation of a service-oriented middle tier that can be accessed by any client application using a standardized data format and request/response mechanism.

For web developers working within the Microsoft stack, and specifically for those who write .NET applications, the best option for building SOA solutions is Windows Communication Foundation. WCF offers a loosely-coupled service architecture with interoperability across multiple platforms and is deeply integrated into the Visual Studio toolset with a great deal of documentation, community support, guidance and best practices freely available.

WCF services expose a set of endpoints that can be accessed via standardized web protocols. Methods are provided as Operation Contracts, in which data objects are exposed as Data Contracts comprised of strongly-typed Data Members. In the context of the pattern being described here, the WCF service accepts asynchronous messages from the client formatted using JavaScript Object Notation (JSON), calls functions within the Entity Framework to pass data to and from SQL server, then returns data to the client as a JSON string. The response is then parsed and bound to UI objects using JQuery. The service can also be used to communicate with SharePoint via the client object model, handle authorization tasks in situations where the client is unable to do so (such as cross-domain calls to Azure ACS for OAuth token processing in Office365) or run any necessary server-side code. As such, it fulfills the role of data broker in the pattern, acting as an agent to handle CRUD operations and signaling messages.

Implementation

The WCF service layer is comprised of several components. First, the service itself, and its associated configuration parameters and bindings, are created within the web application solution (thereby circumventing any cross-domain issues when the service is called by a JavaScript client). A class or classes containing data contracts is then created to map incoming messages to data objects when parameterized operations are required. Finally, a set of operation contracts are defined that can be directly accessed as a URL in an HTTP POST operation and, where necessary, receive complex serialized objects (in cases where no parameters are required the operation contract simply responds to the incoming request on the defined URL). Most importantly, the operation contracts must be able to accept requests and return responses in JSON format so the data can easily be manipulated by a JavaScript client.

NOTE: JSON is not, strictly speaking, required as part of the pattern. XML can also be used to exchange messages; however, JSON is lighter weight and is being widely adopted as the favored approach in many client-side applications.

1

Create a WCF Service

 

Right-click on the web application project in the solution and select Add > New Item > Web > WCF Data Service

 

Eric Shupps Eric Alan Shupps eshupps @eshupps SharePoint Cowboy BinaryWave SmartTrack

 

Add ServiceContract and compatibility attributes to the service class, along with the requisite using statements, and remove the auto-generated DataService type inheritance:

 

using System;

using System.IO;

using System.Net;

using System.Web;

using System.Collections.ObjectModel;

using System.Collections.Generic;

using System.Linq;

using System.Runtime.Serialization;

using System.ServiceModel;

using System.ServiceModel.Activation;

using System.ServiceModel.Web;

using System.Text;

using System.Data.Objects;

using System.Web.Script.Serialization;

using System.Collections.Specialized;

 

namespace BWAppDataModel

{

[ServiceContract(Namespace = "BWAppDataModel")]

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]

public class AppDataService

{

 

}

}

 

2

Edit Configuration Settings

 

Edit the system.ServiceModel node in web.config to specify endpoint behaviors, bindings, and service endpoints. Make sure that the behavior and service names include the project namespace and, in the case of the service endpoints, the data service class name.

 

There are numerous posts on the web about getting WCF to work with various bindings and security configurations. After much trial and error, I have found the following configuration to work flawlessly with IIS and AJAX XMLHttpRequest operatins using both GET and POST operations via JQuery.

 

<system.serviceModel>

<behaviors>

<endpointBehaviors>

<behavior name="BWAppDataModel.DataAspNetAjaxBehavior">

<enableWebScript />

</behavior>

</endpointBehaviors>

</behaviors>

<bindings>

<webHttpBinding>

<binding name="httpBinding" crossDomainScriptAccessEnabled="true">

<security mode="None" />

</binding>

</webHttpBinding>

</bindings>

<services>

<service name="BWAppDataModel.AppDataService">

<endpoint address="" behaviorConfiguration="BWAppDataModel.DataAspNetAjaxBehavior" binding="webHttpBinding" bindingConfiguration="httpBinding" contract="BWAppDataModel.AppDataService" />

</service>

</services>

<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />

</system.serviceModel>

 

NOTE: The sample shows an HTTP endpoint but the SharePoint 2013 framework requires that all apps run over SSL. In addition, the webHttpBinding is configured to support JSONP for cross-domain calls but the sample application adheres to the default same origin policy.

3

Create a Data Contract

 

The entity model created for the sample project includes one data object, the UserInfo table, and two functions – spGetUser and spUpsertUser. The GetUser request does not require a serialized object to be passed to an operation contract – a simple GET operation with a query string parameter will suffice. The UpsertUser function, however, requires a complex object that contains all of the UserInfo properties (fields like UserName, EmailAddress, Phone, etc.). The parameter for the POST operation must be mapped to an object with string data members for each field that precisely match the field names and field order of a JSON object created on the client.

 

To create a data contract for the upsert operation, first create a new class to store the data contracts. Right-click on the web application project and select Add > Class, supplying an appropriate class name such as "DataContracts":

 

Eric Shupps Eric Alan Shupps eshupps @eshupps SharePoint Cowboy BinaryWave SmartTrack

 

Add a using statement for System.Runtime.Serialization and create a public data contract class within the base class:

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.Runtime.Serialization;

 

namespace BWAppDataModel

{

public class DataContracts

{

[DataContract]

public class UserInfoObject

{

 

}

}

}

 

Add variables and DataMember properties to the UserInfoObject class. Each DataMember property must be a string that matches the incoming JSON object:

 

[DataContract]

public class UserInfoObject

{

private string _userId;

private string _loginName;

private string _firstName;

private string _lastName;

private string _email;

private string _phone1;

private string _phone2;

private string _address1;

private string _address2;

private string _city;

private string _state;

private string _zip;

private string _country;

private string _active;

private string _isEngineer;

private string _imageUrl;

 

[DataMember]

public string UserId

{

get { return _userId; }

set { _userId = value; }

}

 

[DataMember]

public string LoginName

{

get { return _loginName; }

set { _loginName = value; }

}

 

[DataMember]

public string FirstName

{

get { return _firstName; }

set { _firstName = value; }

}

 

[DataMember]

public string LastName

{

get { return _lastName; }

set { _lastName = value; }

}

 

[DataMember]

public string Email

{

get { return _email; }

set { _email = value; }

}

 

[DataMember]

public string Phone1

{

get { return _phone1; }

set { _phone1 = value; }

}

 

[DataMember]

public string Phone2

{

get { return _phone2; }

set { _phone2 = value; }

}

 

[DataMember]

public string Address1

{

get { return _address1; }

set { _address1 = value; }

}

 

[DataMember]

public string Address2

{

get { return _address2; }

set { _address2 = value; }

}

 

[DataMember]

public string City

{

get { return _city; }

set { _city = value; }

}

 

[DataMember]

public string State

{

get { return _state; }

set { _state = value; }

}

 

[DataMember]

public string Zip

{

get { return _zip; }

set { _zip = value; }

}

 

[DataMember]

public string Country

{

get { return _country; }

set { _country = value; }

}

 

[DataMember]

public string Active

{

get { return _active; }

set { _active = value; }

}

 

[DataMember]

public string IsEngineer

{

get { return _isEngineer; }

set { _isEngineer = value; }

}

 

[DataMember]

public string ImageUrl

{

get { return _imageUrl; }

set { _imageUrl = value; }

}

}

4

Create Operation Contracts

 

In the data service class, create a new operation contract that accepts and returns JSON. The type of operation, either GET or POST, is specified by the first attribute (WebGet and WebInvoke, respectively) and the WebMessageBodyStyle, RequestFormat and ResponseFormat define the supported message types.

 

namespace BWAppDataModel

{

[ServiceContract(Namespace = "BWAppDataModel")]

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]

public class AppDataService

{

[OperationContract]

[WebGet(BodyStyle = WebMessageBodyStyle.WrappedRequest, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]

public string GetUser(string UserId)

{

 

}

}

}

 

Setup the Entity Model connection by instantiating a new instance of the container within a using statement.

 

[OperationContract]

[WebGet(BodyStyle = WebMessageBodyStyle.WrappedRequest, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]

public string GetUser(string UserId)

{

using (EntityModelContainer entities = new EntityModelContainer())

{

 

}

}

 

 

5

Get Data from Entity Objects

 

Getting data from the entity objects is a simple matter of calling the mapped function and binding the result to a variable. First, create a return response variable, then make a call to the desired function (imported earlier from the matching stored procedure in the database), passing in the strongly-typed parameters required by the procedure and mapping the response to the correct entity object.

 

[OperationContract]

[WebGet(BodyStyle = WebMessageBodyStyle.WrappedRequest, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]

public string GetUser(string UserId)

{

using (EntityModelContainer entities = new EntityModelContainer())

{

string response = string.Empty;

 

try

{

UserInfo user = entities.spGetUser(Convert.ToInt32(UserId)).FirstOrDefault();

 

}

catch (System.Exception ex)

{

 

}

 

return response;

}

}

 

It is often necessary to manipulate the data before returning it to the client. For example, DateTime objects are represented in JSON as "\/Date(1198908717056)\/". This isn’t very conducive for control binding or sorting values in a grid. Instead, it’s often preferable to convert the date into standard "MM/dd/YYYY" format. You may also wish to concatenate fields (such as FirstName and LastName) or add fields that don’t exist in the original dataset. To manipulate the response before it’s serialized, use LINQ to Objects on the collection like so:

 

List<UserInfo> _users = entities.spGetUser(Convert.ToInt32(UserId)).ToList();

 

var users = from u in _users

select new

{

UserId = u.UserID,

LoginName = u.LoginName,

FullName = u.FirstName + " " + u.LastName

};

 

 

Handling POST operations is done in a similar fashion but the contract is defined differently. Instead of WebGet the WebInvoke attribute is used and the parameter is mapped to a DataContract object instead of individually declared query string variables.

 

[OperationContract]

[WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.WrappedRequest, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]

public string UpsertUser(DataContracts.UserInfoObject User)

{

 

}

 

It is important to note that the name of the fields in the DataContract object and the order they appear in must match the JSON object defined on the client or the operation will fail. The process for inserting, updating and deleting data is the same as that used to retrieve data – instantiate the container object, call the function, and get back a typed result (or, if the procedure returns no data, no result at all).

 

[OperationContract]

[WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.WrappedRequest, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]

public string UpsertUser(DataContracts.UserInfoObject User)

{

using (EntityModelContainer entities = new EntityModelContainer())

{

string response = string.Empty;

 

try

{

 

entities.spUpsertUser(Convert.ToInt32(User.UserId),

User.LoginName,

User.FirstName,

User.LastName,

User.Email,

User.Phone1,

User.Phone2,

User.Address1,

User.Address2,

User.City,

User.State,

User.Zip,

User.Country,

Convert.ToBoolean(User.Active),

Convert.ToBoolean(User.IsEngineer),

User.ImageUrl);

 

 

 

}

catch (System.Exception ex)

{

 

}

 

return response;

}

}

 

 

6

Return JSON Data

 

The call to the entity model function will execute the stored procedure and return a typed object; however, the object (or collection of objects), must first be converted to JSON format before it can be sent back the requestor. Fortunately, there is a web script serialization class available to transform the data. First, put the object or object collection into an anonymous type as an array then call the serializer to format the data and assign it to the return variable.

 

[OperationContract]

[WebGet(BodyStyle = WebMessageBodyStyle.WrappedRequest, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]

public string GetUser(string UserId)

{

using (EntityModelContainer entities = new EntityModelContainer())

{

string response = string.Empty;

 

try

{

UserInfo user = entities.spGetUser(Convert.ToInt32(UserId)).FirstOrDefault();

var data = new { user };

JavaScriptSerializer s = new JavaScriptSerializer();

response = s.Serialize(data);

}

catch (System.Exception ex)

{

var data = new { Error = ex.Message };

JavaScriptSerializer s = new JavaScriptSerializer();

response = s.Serialize(data);

}

 

return response;

}

}

 

The result of this transformation will be a string similar to the following:

 

{"d":"{\"user\":[{\"UserId\":\"4fca5677-8e0d-4a72-93c0-eec928379b21\",\"LoginName\":\"jsmith\",

\"FirstName\":\"John\",\"LastName\":\"Smith\",\"Email\":\"jsmith@gmail.com\",

\"Phone1\":\"888-387-1197\",\"Address\":\"611 S. Main Street\",\"State\":\"TX\",\"Country\":\"US\",\"EntityState\":2,\"EntityKey\":{\"EntitySetName\":

\"user\",\"EntityContainerName\":

\"EntityModelContainer\",\"EntityKeyValues\":[{\"Key\":\"UserId\",\"Value\":\"4fca5677-8e0d-4a72-93c0-eec928379b21\"}],\"IsTemporary\":false}}]}"}

 

To handle any exceptions, wrap the operations in a try…catch block and return the exception message in the same serialized format. This can then be processed by the client in a uniform manner to identify any error conditions.

 

In cases where the function does not return any data a success or failure message should still be passed to the client. This can be done by declaring a response object message (such as "Success") inline for serialization. The client can then check for a "Success" or "Error" object and handle each accordingly.

 

[OperationContract]

[WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.WrappedRequest, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]

public string UpsertUser(DataContracts.UserInfoObject User)

{

using (EntityModelContainer entities = new EntityModelContainer())

{

string response = string.Empty;

 

try

{

 

entities.spUpsertUser(Convert.ToInt32(User.UserId),

User.LoginName,

User.FirstName,

User.LastName,

User.Email,

User.Phone1,

User.Phone2,

User.Address1,

User.Address2,

User.City,

User.State,

User.Zip,

User.Country,

Convert.ToBoolean(User.Active),

Convert.ToBoolean(User.IsEngineer),

User.ImageUrl);

 

var data = new { Success = "Success" };

JavaScriptSerializer s = new JavaScriptSerializer();

response = s.Serialize(data);

 

}

catch (System.Exception ex)

{

var data = new { Error = ex.Message };

JavaScriptSerializer s = new JavaScriptSerializer();

response = s.Serialize(data);

}

 

return response;

}

}

 

Regardless of how many functions and operations there are in the service, the syntax is identical for each one – either a WebGet or WebInvoke operation is declared, the container is wrapped in a using statement, a function or other block of code is executed, and the response returned in JSON format. Each new operation is cut and paste from the last with minor alterations as required. This conforms to the original objective of the pattern for new operations to be as simple as copy => paste => tweak => deploy. The process is easy to remember and repeat:

  1. Create stored procedure
  2. Import procedure
  3. Create data contract (if necessary)
  4. Copy and paste either WebGet or WebInvoke operation contract
  5. Modify operation name and parameters (query string variables or data contract object)
  6. Replace function call and return type
  7. Manipulate dataset (if necessary)
  8. Specify response object
  9. Response is serialized and returned

For those who prefer not to use stored procedures, need to access a remote data store (possibly even calling another web service), or just need to execute a batch of code that doesn’t pertain to anything in the entity model, the process remains the same, only the function call and return type are replaced with the requisite code. It’s repetitive almost to the point of being monotonous but that’s the beauty of the pattern – developers don’t need to think about what each operation is doing. They simply make a few edits and carry on to the next operation. As uniform and consistent as this portion of the pattern is, it should even be possible for someone skilled at creating Visual Studio plugins to automatically generate the entire set of data and operation contracts from the entity model and function imports with a simple wizard. That kind of automation would really accelerate app development. For now, the process of cut and paste operation contracts defined in the pattern is at least efficient enough to remove the barriers of working with moderately complex data sets and increase developer productivity.

In Part One of this series a data access layer was creating using the ADO.NET Entity Framework. In Part Three the middle and backend tiers will be exposed to the front-end JavaScript using AJAX calls and JQuery, allowing for a responsive UI with no code-behind or server dependencies. The full Visual Studio solution will be available for download at the conclusion of Part Three.

Additional References

What is Windows Communication Foundation

Getting Started Tutorial for WCF

AJAX Integration and JSON Support

Entity Framework 4.0 and WCF Services 4.0