Logo
programming4us
programming4us
programming4us
programming4us
Home
programming4us
XP
programming4us
Windows Vista
programming4us
Windows 7
programming4us
Windows Azure
programming4us
Windows Server
programming4us
Windows Phone
 
Windows Azure

Service-Orientation with .NET : Entity Abstraction with a .NET REST Service

5/20/2011 5:44:02 PM
- Free product key for windows 10
- Free Product Key for Microsoft office 365
- Malwarebytes Premium 3.7.1 Serial Keys (LifeTime) 2019
Over the course of several years Standard Mold has collected various types of customer-related data. They started out with a simple data model, but different departments added data based on other pre-defined and ad-hoc models, some of which were specific to departmental systems. The resulting disparity led to redundant data and even different terminology used by different departments to refer to the same types of data.

To resolve these problems, several information modeling workshops were arranged and attended by departmental business experts. After some debate and effort, an agreement was reached as to a standard taxonomy used to describe the common business entities and attributes. The resulting entity relationship model is shown in Figure 1.

Figure 1. The Standard Mold entity relationship model.

Standard Mold architects identify the need for a Customer entity service that can be positioned to provide centralized processing of customer-related data and functions. During a service modeling effort, SOA analysts define a Customer service based on the previously defined entity relationship model.

Architects choose to build the Customer service as a REST service to make the service functionality available to consumer programs that do not support SOAP and necessary WS-* standards, and to leverage HTTP caches for performance reasons.

Figure 2 illustrates the planned interaction between the Customer REST service and service consumers.

Figure 2. The message interaction between the consumer and the Customer service.


The service interaction is described as follows:

1.
The service consumer requests a list of customers using a pre-defined address (URI).

2.
In the response from the Customer service, links are embedded that point at resources with the detailed customer information.

3.
The consumer traverses these links and uses the link with relationship type set to “details” to request detailed customer data.

4.
The response from the Customer service contains a link to meetings for the selected customer.

5.
The consumer again traverses the links in the response and uses the link marked with “meetings” to request the meeting resource for the current customer.

Before the conversation between the Customer service and the service consumer begins, the Customer service only provides a service entry point. The consumer then traverses links in the response documents to find related resources. This gives the Customer service the freedom to change both how related resources are named and arranged. Even the servers and domains of the uri:s can be changed without prior notice. The only thing that the Customer service cannot change without communicating with its consumers in advance is the URI of the entry point.

Standard Mold architects and developers carried out several specific steps to ensure that the Customer REST service behaves as depicted in Figure 1.

First, they created a service contract using WCF attributes:

Example 1.
[ServiceContract]
public interface ICustomerServiceRest
{
[OperationContract]
[WebInvoke(
Method = "PUT",
BodyStyle = WebMessageBodyStyle.Bare,
ResponseFormat = WebMessageFormat.Xml,
UriTemplate = "/customers/create"
)]
void CreateCustomer(Customer customer);
[OperationContract]
[WebInvoke(
Method = "POST",
BodyStyle = WebMessageBodyStyle.Bare,
ResponseFormat = WebMessageFormat.Xml,
UriTemplate = "/customers/{customerId}"
)]
void UpdateCustomer(string customerId,Customer customer);
[OperationContract]
[WebInvoke(
Method = "DELETE",
UriTemplate = "/customers/{customerId}"
)]
void DeleteCustomer(string customerId);
[OperationContract]
[WebGet(UriTemplate = "/customers/{customerId}")]
Atom10FeedFormatter GetCustomerDetails(string customerId);
[OperationContract]
[WebGet(UriTemplate = "/customers")]
System.ServiceModel.Syndication.
Atom10FeedFormatter GetCustomers();
[OperationContract]
[WebGet(UriTemplate = "/customers/{customerId}/meetings")]
Atom10FeedFormatter GetCustomerMeetings(string customerId);
}


The GetCustomers method acts as the entry point to the service and is called using the “customers” relative URI and the GET verb:

Example 2.
public Atom10FeedFormatter GetCustomers()
{
List<Models.Customer> customers = DB.GetCustomers();
if (customers != null)
{
Uri uri = OperationContext.Current.
IncomingMessageHeaders.To;
Atom10FeedFormatter formatter = null;
SyndicationFeed feed = new SyndicationFeed();
feed.LastUpdatedTime = DateTime.Now;
feed.Id = WebOperationContext.Current.IncomingRequest.
UriTemplateMatch.RequestUri.ToString();
feed.Title = new TextSyndicationContent("Customers");
feed.AddSelfLink(uri);
List<SyndicationItem>
items = new List<SyndicationItem>();
foreach (var customer in customers)
{
SyndicationItem item = new SyndicationItem();
item.Title = new TextSyndicationContent
(customer.CompanyName);
item.Content = SyndicationContent.
CreateXmlContent(customer);
SyndicationLink detailsLink =
new SyndicationLink
(
new Uri
(
OperationContext.Current.
IncomingMessageHeaders.To.AbsoluteUri
+ "/" + customer.CustomerId
)
);
detailsLink.RelationshipType = "details";
detailsLink.Title = "Details";
item.Links.Add(detailsLink);
items.Add(item);
}
feed.Items = items;
formatter = new Atom10FeedFormatter(feed);
WebOperationContext.Current.
OutgoingResponse.ContentType
= Microsoft.ServiceModel.Web.ContentTypes.Atom;
return formatter; }
WebOperationContext.Current.
OutgoingResponse.SetStatusAsNotFound();
return null;
}


The DB.GetCustomers method fetches a list of customer records from the database. In this list, only two of the customer fields (CompanyName and CustomerId) are populated. This is a performance optimization technique used by Standard Mold developers, because if all details of the Customer entity are returned in a list, it would likely introduce unnecessary runtime processing and bandwidth consumption.

After retrieving the customer list, the Atom Publishing Protocol is used to represent the resource. This protocol was chosen because it allows for the embedding of links in resource descriptions in a standardized fashion so that they can be easily found by service consumers.

A details link is added to the customer list that consumers can use to obtain detailed information about a given customer. The XML output from the GetCustomers method looks like this:

Example 3.
<feed xmlns="http://www.w3.org/2005/Atom">
<title type="text">Customers</title>
<id>http://standardmold/customerservice/customers</id>
<updated>2010-01-10T17:34:20+01:00</updated>
<link rel="self" type="application/atom+xml" href=
"http://standardmold/customerservice/customers"/>
<entry>
<id>uuid:445cf20f-4eb0-4772-bad5-a44b51c48145;id=91</id>
<title type="text">WoodGroove Ltd</title>
<updated>2010-01-10T16:34:20Z</updated>
<link rel="details" title="Details" href=
"http://standardmold/customerservice/customers/91"/>
<content type="text/xml">
<Customer xmlns=
"http://schemas.datacontract.org/2004/07/
EntityService.Models"xmlns:i=
"http://www.w3.org/2001/XMLSchema-instance">
<CompanyName>WoodGroove Ltd</CompanyName>
<CustomerId>91</CustomerId>
</Customer>
</content>
</entry>
<entry>
<id>uuid:445cf20f-4eb0-4772-bad5-a44b51c48145;id=92</id>
<title type="text">Microsoft </title>
<updated>2010-01-10T16:34:20Z</updated>
<link rel="details" title="Details" href=
"http://standardmold/customerservice/customers/92"/>
<content type="text/xml">
<Customer xmlns="http://schemas.datacontract.org
/2004/07/EntityService.Models" xmlns:i=
"http://www.w3.org/2001/XMLSchema-instance">
<CompanyName>Microsoft</CompanyName>
<CustomerId>92</CustomerId>
</Customer>
</content>
</entry>
<entry>
<id>uuid:445cf20f-4eb0-4772-bad5-a44b51c48145;id=93</id>
<title type="text">First Office</title>
<updated>2010-01-10T16:34:20Z</updated>
<link rel="details" title="Details" href=
"http://standardmold/customerservice/customers/93"/>
<content type="text/xml">
<Customer xmlns="http://schemas.datacontract.org
/2004/07/EntityService.Models" xmlns:i=
"http://www.w3.org/2001/XMLSchema-instance">
<CompanyName>First Office</CompanyName>
<CustomerId>93</CustomerId>
</Customer>
</content>
</entry>
<entry>
<id>uuid:445cf20f-4eb0-4772-bad5-a44b51c48145;id=94</id>
<title type="text">Bosna</title>
<updated>2010-01-10T16:34:20Z</updated>
<link rel="details" title="Details" href=
"http://standardmold/customerservice/customers/94"/>
<content type="text/xml">
<Customer xmlns="http://schemas.datacontract.org
/2004/07/EntityService.Models" xmlns:i=
"http://www.w3.org/2001/XMLSchema-instance">
<CompanyName>Bosna</CompanyName>
<CustomerId>94</CustomerId>
</Customer>
</content>
</entry>
</feed>


Sample service consumer code is developed to test the traversing of the XML to find the details link.

First the GetDetailsLink method is created:

Example 4.
private static Uri GetDetailsLink(Uri entryPoint, string customerId)
{
using (AtomPubClient atomHttpClient = new AtomPubClient())
{
SyndicationFeed feeds = atomHttpClient.GetFeed(entryPoint);
foreach (SyndicationItem customerFeedItem in feeds.Items)
{
foreach (SyndicationLink customerItemLink
in customerFeedItem.Links)
{
if
(
customerItemLink.RelationshipType.
Equals("details") &&
customerItemLink.Uri.ToString().
EndsWith(customerId)
)
{
return customerItemLink.Uri;
}
}
}
}
return null;
}


A test run of the service consumer retrieves the details link for customer 94 by calling the method with the following parameters:

Example 5.
Uri customerDetailsLink
= GetDetailsLink(new
Uri("http://standardmold/customerservice/customers"),
"94");

When the service consumer uses this link to request customer details, the GetCustomerDetails method of the Customer service is executed. The implementation of this method is similar to the GetCustomers method in that it uses the Atom protocol and adds links to the customer details data.

Example 6.
public Atom10FeedFormatter GetCustomerDetails(string customerId)
{
Customer customer = DB.GetCustomerById
(Int32.Parse(customerId));
if (customer != null)
{
List<SyndicationItem> items = new
List<SyndicationItem>();
SyndicationItem item = new SyndicationItem();
item.Title = new TextSyndicationContent
(customer.CompanyName);
item.Content = SyndicationContent.
CreateXmlContent(customer);
SyndicationLink editLink = new SyndicationLink
(
WebOperationContext.Current.
IncomingRequest.UriTemplateMatch.RequestUri
);
editLink.RelationshipType = "edit";
editLink.Title = "Edit";
item.Links.Add(editLink);
SyndicationLink meetingLink = new SyndicationLink
(
new Uri
(
WebOperationContext.Current.IncomingRequest.
UriTemplateMatch.RequestUri.ToString() + "/meetings"
)
);
meetingLink.RelationshipType = "meetings";
item.Links.Add(meetingLink);
items.Add(item);
SyndicationFeed feed = new SyndicationFeed
{
Id = WebOperationContext.Current.IncomingRequest.
UriTemplateMatch.RequestUri.ToString(),
Title = new TextSyndicationContent
(customer.CompanyName)
};
feed.AddSelfLink
(WebOperationContext.Current.
IncomingRequest.GetRequestUri());
feed.Items = items;
Atom10FeedFormatter formatter = new
Atom10FeedFormatter(feed);
WebOperationContext.Current.
OutgoingResponse.ContentType
= Microsoft.ServiceModel.Web.ContentTypes.Atom;
return formatter;
}
WebOperationContext.Current.
OutgoingResponse.SetStatusAsNotFound();
return null;
}


This time, several links are added to the only entry in the feed. This entry contains detailed customer information and the link to related meetings.

Here is the XML output from this method:

Example 7.
<feed xmlns="http://www.w3.org/2005/Atom">
<title type="text">Bosna</title>
<id>http://standardmold/customerservice/customers/94</id>
<updated>2010-01-10T16:36:46Z</updated>
<link rel="self" type="application/atom+xml"
href="http://standardmold/customerservice/
customers/94"/>
<entry>
<id>uuid:445cf20f-4eb0-4772-bad5-a44b51c48145;id=94</id>
<title type="text">Bosna Jedina</title>
<updated>2010-01-10T16:36:46Z</updated>
<link rel="edit" title="Edit"
href="http://standardmold/customerservice/
customers/94"/>
<link rel="meetings"
href="http://standardmold/customerservice/
customers/94/meetings"/>
<content type="text/xml">
<Customer xmlns="http://schemas.datacontract.org/
2004/07/EntityService.Models"
xmlns:i="http://www.w3.org/
2001/XMLSchema-instance">
<Cellular>+387(0)63912910</Cellular>
<CompanyName>Bosna</CompanyName>
<CustomerId>94</CustomerId>
<Fax>+387(0)63912911</Fax>
<Phone>+387(0)79900900</Phone>
<TimeStamp>AAAAAAAAD6I=</TimeStamp>
</Customer>
</content>
</entry>
</feed>


The following method is created to get a meetings link from a customer details response:

Example 8.
private static Uri GetMeetingsLink(Uri customerDetailsUri)
{
using (AtomPubClient atomHttpClient = new AtomPubClient())
{
SyndicationFeed feeds = atomHttpClient.GetFeed
(customerDetailsUri);
foreach (SyndicationItem customerFeedItem in feeds.Items)
{
foreach (SyndicationLink customerItemLink
in customerFeedItem.Links)
{
if (customerItemLink.RelationshipType.Equals("meetings"))
{
return customerItemLink.Uri;
}
}
}
}
return null;
}

To obtain this link, the consumers use the URI returned by the GetDetailsLink method:

Uri meetingsUri = GetMeetingsLink(customerDetailsLink);

The URI points at the meetings resource for company 94, and the consumer is able to get the desired list of meetings.

The XML output by this method is as follows:

Example 9.
<feed xml:base="http://standardmold/customerservice/
customers/94/meetings" xmlns="http://www.w3.org/2005/Atom">
<title type="text">Customers Metting, CustomerId=94</title>
<id>http://standardmold/customerservice/
customers/94/meetings</id>
<updated>2010-01-10T17:38:13+01:00</updated>
<entry>
<id>uuid:445cf20f-4eb0-4772-bad5-a44b51c48145;id=7</id>
<title type="text">Meeting Id:1</title>
<updated>2010-01-10T16:38:13Z</updated>
<content type="text/xml">
<Meeting xmlns="http://schemas.datacontract.org/
2004/07/EntityService.Models"
xmlns:i="http://www.w3.org/2001/
XMLSchema-instance">
<customerId>94</customerId>
<dateofmeeting>2010-10-10T00:00:00</dateofmeeting>
<meetingId>1</meetingId>
<notes>

It’s our experience that taking personal notes at meetings somehow breeds respect and approval from superiors, and managers enjoy having a simple list of bullet points and action items at-hand. The long and short—if you have to suffer through the meeting anyways, make it a habit to provide a valuable account to meeting organizers... you will be rewarded with respect and trust.

Example 10.
        </notes>
</Meeting>
</content>
</entry>
<entry>
<id>uuid:445cf20f-4eb0-4772-bad5-a44b51c48145;id=8</id>
<title type="text">Meeting Id:2</title>
<updated>2010-01-10T16:38:13Z</updated>
<content type="text/xml">
<Meeting xmlns="http://schemas.datacontract.org/
2004/07/EntityService.Models"
xmlns:i="http://www.w3.org/2001/
XMLSchema-instance">
<customerId>94</customerId>
<dateofmeeting>2010-11-11T00:00:00</dateofmeeting>
<meetingId>2</meetingId>
<notes>
We started on time with 3 members and the other team members
arrived shortly thereafter
</notes>
</Meeting>
</content>
</entry>
</feed>


A method that can obtain this XML from the Customer service and return a list of Meeting objects looks like this:

Example 11.
private static List<Meeting> GetCustomerMeetingList(Uri meetingsUri)
{
var customerMeetings = new List<Meeting>();
using (AtomPubClient atomHttpClient = new AtomPubClient())
{
if (meetingsUri != null)
{
SyndicationFeed customerMeetingsFeed =
atomHttpClient.GetFeed(meetingsUri);
foreach (var item in customerMeetingsFeed.Items)
{
var customerMeetingXmlContent = item.Content
as XmlSyndicationContent;
if (customerMeetingXmlContent != null)
{
var customerMeeting =
customerMeetingXmlContent.
ReadContent<Meeting>();
customerMeetings.Add(customerMeeting);
}
}
}
}
return customerMeetings;
}


To actually get the list of meetings, the service consumer calls the method using the meeting list URI that was returned from the GetMeetingsList:

List<Meeting>meetingList=GetCustomerMeetingList(meetingsUri);

With a preliminary version of the service architecture completed, Standard Mold architects decide to make further improvements by ensuring that customer data is not inadvertently overwritten in the database. A timestamp column is added and updated via a SQL server stored procedure that updates customer information only if the timestamp in the database remains unchanged:

Example 12.
CREATE PROCEDURE [dbo].[UpdateCustomer]
@id as uniqueidentifier,
@cName as nvarchar(50),
@phone as nvarchar(50),
@fax as nvarchar(50),
@cell as nvarchar(50),
@updateTag as timestamp
AS
BEGIN
BEGIN TRY
SET NOCOUNT ON;
--do not update if [TimeStamp] has been changed
UPDATE dbo.Customer WITH (ROWLOCK)
SET
[CompanyName] = @cName,
[Phone] = @phone,
[Fax] = @fax,
[Cellular] = @cell
WHERE [CustomerId] = @id
AND [TimeStamp] = @updateTag
END TRY

BEGIN CATCH
SELECT ERROR_NUMBER(), ERROR_LINE(),ERROR_MESSAGE()
END CATCH
END


With a simple check in the data access code, it is possible to find out how many rows are affected. If the result is 0 rows, then something went wrong. Most likely the customer was changed by another request before the current request could make the update.

The code that allows this to be discovered can be found inside the UpdateCustomer method. It uses the Enterprise Library Data Access Block in order to make the database access logic more compact than it would be had it been written using ADO.NET directly:

Example 13.
public bool UpdateCustomer(Customer customer)
{
using
(
var dbCommand =
Database.GetStoredProcCommand
(StoredProcedures.UPDATECUSTOMER)
)
{
Database.AddInParameter
(
dbCommand, "@id", DbType.Guid, customer.CustomerId
);
Database.AddInParameter
(
dbCommand, "@cName", DbType.String,
customer.CompanyName
);
Database.AddInParameter
(
dbCommand, "@phone", DbType.String,
customer.Phone
);
Database.AddInParameter
(
dbCommand, "@fax", DbType.String,
customer.Fax
);
Database.AddInParameter
(
dbCommand, "@cell", DbType.String,
customer.Cellular
);
Database.AddInParameter
(
dbCommand, "@updateTag", DbType.Binary,
customer.TimeStamp
);
int rowsAffected =
Database.ExecuteNonQuery(dbCommand);
return rowsAffected == 1;
}
}


If the UpdateCustomer method returns “false,” the update was not successful. This should normally be communicated back to the service consumer, as follows:

Example 14.
public void UpdateCustomer
(EntityService.Models.Customer customer)
{
try
{
bool updated = DB.UpdateCustomer(customer);
if (updated)
WebOperationContext.Current.
OutgoingResponse.StatusCode
= System.Net.HttpStatusCode.OK;
else
WebOperationContext.Current.
OutgoingResponse.StatusCode
= System.Net.HttpStatusCode.Conflict;
}
catch
{
... exception handling code ...
WebOperationContext.Current.OutgoingResponse.StatusCode
= System.Net.HttpStatusCode.InternalServerError;
}
}


The consumer of the Customer service will get an HTTP response with the following status if the optimistic lock failed:

409 Conflict

This is exactly what is expected of a REST service in this situation. Standard Mold developers, however, choose to add more information to the response so that service consumers can understand the nature of the conflict.

If an unexpected exception occurs, the service consumer will instead receive the following status, which communicates that there is a problem in the service:

500 Internal server error

If everything proceeds as expected the consumer will get the response code:

200 OK
Other -----------------
- Service-Orientation with .NET : Utility Abstraction with a .NET Web Service
- Service-Orientation with .NET : Service Reusability and the Separation of Concerns
- Service-Orientation with .NET : Service Discoverability
- Service-Orientation with .NET : Exception Shielding
- Service-Orientation with .NET : Service Abstraction & Validation Abstraction
- Service-Orientation with .NET : Service Loose Coupling and Service Capability Granularity
- Service-Orientation with .NET : Service Façade
- Service-Orientation with .NET : Decoupled Contract
- Service-Orientation with .NET : Service Loose Coupling
- Service-Orientation with .NET : Service Contracts and Interoperability - Canonical Protocol
 
 
Top 10
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 2) - Wireframes,Legends
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 1) - Swimlanes
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Formatting and sizing lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Adding shapes to lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Sizing containers
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 3) - The Other Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 2) - The Data Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 1) - The Format Properties of a Control
- Microsoft Access 2010 : Form Properties and Why Should You Use Them - Working with the Properties Window
- Microsoft Visio 2013 : Using the Organization Chart Wizard with new data
 
programming4us
Windows Vista
programming4us
Windows 7
programming4us
Windows Azure
programming4us
Windows Server