Home > 2010 Fall DPS907 > Data access for WCF WebHttp Services

Data access for WCF WebHttp Services

November 3, 2010 Leave a comment Go to comments

At this point in time, you have created your first WCF WebHttp Service. In this post, intended as an entry-level tutorial, we add a data access layer to the service.

Right at the beginning here, we need to state a disclaimer:

Like many technologies, WCF WebHttp Services offers functionality that spans a wide range, from entry-level needs, through to high-scale, multi-tier, enterprise-class requirements. This post does NOT attempt to cover the range.

Instead, we intend to provide an entry-level tutorial, to enable the developer to build a foundation for the future, where the complexity will be higher. We are intentionally limiting the scope and use case of the topics covered here, so that the new WCF service developer can build on what they know.

.

WCF WebHttp Services, as delivered in .NET Framework 4, are still fairly new as of this writing (in October 2010). The MSDN documentation is typically very good. However, the information needed by us is scattered all over the documentation, and there’s no set of tutorials – by Microsoft or others – that enable the new WCF service developer to get success quickly. The blog posts by the ADO.NET Entity Framework and WCF teams are good, but the examples tend to be focused on a specific scenario or configuration.

We need a tutorial that is more general, consistent, and predictable – a tutorial that enables the developer to reliably add a data access layer to a WCF WebHttp Service, and get it working successfully. Hopefully, this tutorial meets these needs.

.

Topics that this post will cover

In the last blog post, titled “WCF WebHttp Services introduction”, you learned about the programming model, and got started by creating a simple project from the template. Make sure you cover that material before you continue here.

This  blog post will cover the following topics:

  • Principles for adding a data access layer to a WCF WebHttp Service
  • Brief introduction to POCO
  • A brief introduction to the enabling C# language features (generics, LINQ, lambdas)
  • Getting started by creating a data-enabled service

.

Principles for adding a data access layer to a WCF WebHttp Service

The Entity Framework is the recommended data access solution for WCF WebHttp Services.

.

Review – Entity Framework data model in a WCF Data Service

In the recent past, you gained experience with the creation of Entity Framework data models, as you programmed WCF Data Services. That programming model (WCF Data Services) is designed to expose a data model in a RESTful manner, using the OData protocol.

A feature of the OData protocol is that the data is delivered to the consumer in AtomPub XML, or in JSON. Let’s focus on the XML for a moment.

The AtomPub XML that expresses the entities is “heavy” – it includes the AtomPub wrapping structure, schema namespace information, and related attributes for the properties and values.

The image below is the XML that’s returned by a WCF Data Service, for the Northwind “Categories” collection. Only the first object in the collection is shown. As you can see, the actual data for the Category object is near the bottom, in about four lines of code.

.

Preview – Entity Framework data model in a WCF WebHttp Service

Now, let’s preview the use of an Entity Framework data model in a WCF WebHttp Service.

The default XML format for an entity, or a collection of entities, is known as entity serialization. It too is “heavy”, although not as heavy as WCF Data Service AtomPub XML. Entity serialization includes schema namespace information, and related attributes for the properties and values.

The image below is the XML that’s returned by a non-optimized WCF WebHttp Service, for the Northwind “Categories” collection. Only the first object in the collection is shown. As you can see, the actual data for the Category object is near the bottom, in about four lines of code.

WCF WebHttp Services should consume and provide data in easy-to-use concise formats. Therefore, we optimize the entity serialization process, by performing one additional step after we create our Entity Framework data model: We generate POCO classes. Generating plain old CLR classes (POCO) enables WCF to serialize (and deserialize) objects in a very concise XML format.

The image below is the XML that’s returned by a WCF WebHttp Service that has had POCO classes generated for the data model, for the Northwind “Categories” collection. Only the first object in the collection is shown. As you can see, most of the XML describes the actual data for the Category object.

The following is a summary, a side-by-side comparison, of these three XML formats:

.

AtomPub EF serialization POCO serialization

.

Brief introduction to POCO

In this post, we provide an entry-level coverage of POCO class generation and usage. Please note that more work must be done, and more knowledge gained, to use Entity Framework data models in complex service applications.

POCO, as noted above, is an initialism for plain old CLR object. A POCO (object) is often a simple class, although its definition can grow complex. The following is an image of the POCO class that was generated (by the Visual Studio 2010 tool) for the Northwind Category entity:

POCO class generation and use yields a number of benefits and advantages. As covered earlier, one benefit is that the data packaging scheme is uncomplicated and understandable.

To generate POCO classes for your Entity Framework data model, perform the following general steps:

  1. In Visual Studio 2010, open your Entity Framework data model
  2. On the design surface, right-click, and choose “Add Code Generation Item…”
  3. In the Add New Item dialog, select Installed Templates > Visual C# > Code
  4. In the middle-pane list, choose/select “ADO.NET POCO Entity Generator”
  5. Enter a name (e.g. Northwind), and click the “Add” button

Visual Studio 2010 will then generate the POCO classes. On a typical computer, you must acknowledge the dialogs that appear (which warn you about running code); it is OK to continue.

.

Note – The POCO template must be installed on the computer. If it does not appear, then click Online Templates, and optionally enter “POCO” in the upper-right search text box. When it shows you the template, you can select it for download and installation.

.

A brief introduction to the enabling C# language features

In this section, we will cover the following:

  • Object context
  • Object set
  • C# generics
  • LINQ – language integrated query
  • Lambda expressions

.

Object context

An object context is an instance of the ObjectContext class.

In code, you create an object context as a new instance of your Entity Framework data model’s “entity container”. (The entity container is a container/wrapper for a set of entities.) For example, we recently have been working with a copy of the Northwind database, and we created an Entity Framework data model for it. Its entity container name was “MyNorthwindEntities”. Therefore, the code would be:

var context = new MyNorthwindEntities();

By convention, we typically use “context” as the instance name. In addition, as a programming best practice, we wrap the creation in a using {} statement block, so that the resource is released as soon as possible.

The object context provides access to the entity types in your Entity Framework data model. The object context is a “work area” for the operations you perform on the entity types. For example:

  • The object context holds the results of a fetch
  • It is the target for add, change, and remove operations

.

When you perform an add, change, or remove operation on the object context, you must save it, which will persist the change in the data store.

An object context encapsulates a connection to the data store, metadata about the model, and a set of methods to support its functionality.

Study these references for more on the object context.

ObjectContext Class (MSDN)

.

Object set

You have just learned that the object context provides access to the entity types in your model. Each entity type appears as a property of the object context. The type of the property is ObjectSet.

An object set is the target for read, add, change, and remove operations. For example, if you have an object context instance named “context”, and you wish to read the “Customers” entity as a generic list (of Customer objects), you would call this method:

context.Customers.ToList()

Skim this reference to view the wide range of operations that are supported on an object set:

ObjectSet<TEntity> Class (MSDN)

.

Generics

C# generics enable you to define and use data structures (e.g. objects) in a type-safe manner, and perform operations on the data structures without the need to define an operation implementation for each allowable type. Generics also help the developer avoid cumbersome or inconvenient cast/convert syntax and operations, and code bloat (from having to define type-specific code blocks).

Generics were added to C# in 2005.

In this course, and in its prerequisite course (INT422/BTI420), we use generics most often with collection classes. This need matches up very well with the fact that we are working with Entity Framework data model entity types. The advantage of generics is that we do not have to define type-specific methods or operations. We use the same generic type (e.g. List<T>) to refer to a collection of “Customers”, or to a collection of “Products”.

The compiler and runtime provide type safety, and therefore error-free coding and execution. However, do not think that you will lose access to type-specific members (like the specific property names in “Customer”, like “CustomerName”, “City”, and so on). Instead, with a generic, you are assured of being able to use/access the type-specific members, as first-class members.

For more, read/skim the following article, and the articles it links to:

Introduction to Generics (C# Programming Guide) (MSDN)

.

LINQ – language integrated query

LINQ is a C# language feature that supports in-language/in-band operations on data. The operations – known as query expressions – include the full range of read, add, change, and remove. The data store can include a wide range of possibilities, from in-memory object collections, to the file system, a relational database, and a remote/cloud store.

The LINQ feature was added to C# in 2007.

LINQ defines a set of language extensions, most notably the following:

  • A query syntax
  • Implicitly-typed variables
  • Anonymous types
  • Object initializers
  • Lambda expressions

.

The LINQ syntax defines a set of query operators. For those familiar with relational database querying, the LINQ query operators will appear familiar. A typical query expression (that follows the LINQ pattern) consists of these parts:

from …
where …
orderby …
select …

For examples, study the “Getting started…” section of the LINQ article by Hejlsberg and Box (linked below).

LINQ is not an interim technology, and it will not be replaced in a future version of the language. It is here to stay, so you cannot avoid learning LINQ and using it in your code. As noted earlier, it supports operations on data. Well, you do that in almost every one of your C# programs. So, learn it, and use it.

To get started learning about LINQ, study these resources:

.

LINQ: .NET Language Integrated Query, by Anders Hejlsberg and Don Box

Be sure to read at least these sections:

  • .NET Language-Integrated Query
  • Getting Started with Standard Query Operators

.

Wikipedia LINQ article , particularly the Language extensions section

Be sure to pay special attention to this section:

Language extensions

.

Lambda expressions

Many LINQ query operators enable the user to provide/supply a function, as an argument. The function could extract, filter, or project – the developer decides its behaviour.

The function syntax is known as a lambda expression in C#. (It is often referred to as an inline method. More generically, it is known as an anonymous function.) The syntax below shows a lambda expression that returns an entity object that matches a specific condition:

c => c.CustomerID == 23

The return type is inferred from the context in which it is used. In other words, if the lambda expression is used as an argument to a LINQ query operator that is supposed to return an entity object, then the return type of the lambda expression above is the entity object type.

Anonymous functions can “see” the local variables in the surrounding methods. Repeating the example syntax above, assume that the method that surrounds the lambda expression includes a local variable named “custID”, and the variable held the value “23”. The new syntax would be:

c => c.CustomerID == custID

How do you read or pronounce this syntax? As suggested by Hejlsberg and others (see this StackOverflow article), it can be read as any one of the following:

c becomes (the result of) c.CustomerID equals custID

c for which c.CustomerID is equal to custID

c such that c.CustomerID is equal to custID

c maps to c.CustomerID equals custID

c lambda of c dot CustomerID is equal to custID

.

This will get you started with lambda expressions. For more, study the “Lambda Expressions and Expression Trees” section of the LINQ article by Hejlsberg and Box.

.

Getting started by creating an entry-level but full-featured service

Now, we’re ready to create an entry-level, but full-featured WCF WebHttp Service. This service will support all of the HTTP methods, GET, POST, PUT, and DELETE.

Create a new WCF WebHttp Service project (perhaps named FirstServiceWithEF).

Perform the configuration changes that were detailed in the “Getting Started with WCF WebHttp Services” section of the blog post titled “WCF WebHttp Services introduction. (For the virtual path, perhaps you can specify /firstef.) Run/test your work before we continue.

Now that you know that the service works, go back to the Service1.cs source code. Either comment out all the existing methods, or delete them.

.

Add an Entity Framework data model to the project

Add an Entity Framework data model to the project (perhaps named Model.edmx). Configure the data model with these settings:

  • Generate from (your copy of the) Northwind database
  • Save the entity connection settings as MyNorthwindEntities (which will become the EntityContainer name too)
  • Choose the Categories, Products, and Suppliers tables
  • For your model name, use MyNorthwindModel

.

Generate the POCO classes

Before you close the Entity Framework data model design surface, let’s generate the POCO classes.

Follow the steps in the section above, titled “Brief introduction to POCO”, and then continue here.

At this point in time, the items in your project should include the list that’s shown in the image to the right.

The classes under the “Northwind.tt” branch are the generated POCO classes (plus a class used for maintenance).

The class under the “Northwind.Context.tt” branch is the object context, which will be described soon.

You can now save and close Model.edmx. (Note that every time you make a change to the data model, you are prompted to generate the POCO classes again. That’s OK – it’s normal behaviour.)

.

Add a method that will GET a collection of Suppliers

We’re now ready to add a method to our Service1.cs source code file. The method will return a collection of Supplier objects.

For the first time, we must design a URI template. In this situation, we simply want a collection of Supplier objects. Therefore, we will decide to use “Suppliers” as our URI template string. As noted elsewhere, this string is case-insensitive.

Follow the coding pattern for the existing GetCollection method, and create a GetSuppliers method. It should look something like the following:

Whenever we want to work with our data model, we use an object context, which was described earlier. As a reminder, an object context provides access to the entity types in your Entity Framework data model. The object context is a “work area” for the operations you perform on the entity types.

The right way to use a context is to initialize it in a using statement, which ensures that the context is disposed of properly at the end of the code block.

The typical usage pattern in context operations looks like this:

context.EntitySet.Action()

  • “context” is the instance of the object context
  • “EntitySet” is the name of the entity set that we want to work with
  • “Action()” is the method we want to call; note that these methods can be chained/stacked (e.g. Action1().Action2().Action3())

.

Typical actions/methods that we will regularly use include First(), ToList(), Where(), OrderBy(), and Count(), as well as AddObject(), and DeleteObject().

Now, build your project. Run (start without debugging, Ctrl+F5). Uh-oh… “Internet Explorer cannot display the webpage”. If you were to enable tracing, and examined the trace log, you would see that the connection was unexpectedly terminated, because the server closed the connection.

What is causing this error? One (or both) of the following XML serialization issues:

  • A feature called “lazy loading” is enabled
  • “POCO proxy class generation” is enabled

Either or both can be corrected. However, correcting them now, while appropriate for this situation, is not a practical solution for all other situations. Making the corrections will work for us now, and enable us to continue, learn, and have some success.

To correct the “lazy loading” issue, open your data model. Right-click the design surface (over an open/empty area), and choose Properties. By default, the Lazy Loading Enabled setting is True. Change it to False. Save and close the data model (and consent to POCO class generation again).

Build and run your project. If it still won’t display XML, then you’ll have to correct the “POCO proxy class generation” issue. This is typically the situation for methods that return a collection of objects. To correct the issue, set the ContextOptions.ProxyCreationEnabled property to false, in code, in the method body. It should now look something like the following:

Build and run your project again. This time, you should see the Suppliers object collection returned as an XML document.

Background on “lazy loading”:

Lazy loading is a feature that delays the loading of a navigation property (which could be an object, or an object set) until the object/object set is assertively dereferenced. This is often a good thing, as it is performant and conserves resources.

However, it can be a problem during serialization of an object/object set (e.g. to xml or json). The WCF serializer attempts to dereference the navigation properties, which will cause an endless dereferencing loop. This is because the dereferenced object itself includes a navigation property that is the object being serialized.

For typical operations in entry-level applications, we suggest that you disable lazy loading as described above.

.

Background on “POCO proxy class generation”:

The downloadable POCO class generation template does its work in a certain manner. The result is that at runtime, a “proxy class” is generated, which conceptually sits between the Entity Framework entity class, and the generated POCO class. However, the benefits of this arrangement do not apply to our scenario, where we are using an Entity Framework data model with a WCF WebHttp Service.

There are two ways to disable proxy class generation. The first way is to disable it in the using {} statement block, as shown above. The other way, which is beyond an entry-level treatment, is to edit the T4 template (that generates the POCO classes), and remove the “virtual” modifier for the properties.

.

Add a method that will GET a specific Supplier (i.e. it takes an argument)

In this section, we will add another method. It will take an argument, and will enable us to GET a specific supplier. For now, we will not implement rigorous error-checking, and will assume that the passed-in argument – which appears as a URI fragment – contains correct and actionable data.

The importance of URI template design surfaces here for the first time. You must decide on a suitable URI format. What are the options? There are at least two options.

  1. An OData-style URI – http://localhost:8080/firstef/suppliers(8)
  2. A “hackable”-style URI – http://localhost:8080/firstef/suppliers/8

In this post, we will use the second style.

Create a new method, perhaps called GetSupplier. It will take a string argument, because that is the data type of the URI at the server.

The URI template for the method has a placeholder syntax, curly braces. The name/token inside the curly braces must match the name of the method argument.

The method argument is a string. However, the Suppliers entity has an identity property – SupplierID – which is an int. We have to convert the string to an int, before we perform a lookup.

Note that the name/token is the same for both the URI template, and the method argument. This is a requirement.

Next, we initialize an object context. Finally, we fetch the specific supplier.

Notice the “First” method’s expression. It’s a lambda expression, which was introduced earlier. We read it as “s for which s.SupplierID is equal to lookupID”.

Our GetSupplier method appears below.

Build and run (using the URI from above, http://localhost:8080/firstef/suppliers/8). Uh-oh (again) – an error message, but this time, it looks a bit different.

Go back and configure the context (to disable proxy class generation). Then, build and run, and you’ll see XML for the one specific supplier.

Methods that serve GET requests for other resources can be constructed to meet your needs. Go ahead and create some more, and experiment.

.

Add a method (or two) to get suppliers from a specific country

To illustrate LINQ query expressions and lambda expressions, we’ll add a method (or two) that enables the user to request suppliers from a specific country.

The URI template will look like this: /Suppliers/Country/{country}

The coding pattern will be similar to the above examples. The completed methods are shown below.

.

Continue by adding POST, PUT, and DELETE methods

Now it’s time to make changes to the data store. There are a few things to think about:

  • Context
  • URI template
  • Feedback to the requestor

.

As you’ve learned, the context is a work area. Conceptually, we make changes to the context, by (for example) creating a new Supplier. Then what? Well, if we save the changes to the context, the new Supplier will be persisted in the data store. When we did GET (i.e. data fetches) operations, we didn’t care about the changes to the context. Now we do.

A now-familiar issue is the URI template design for the methods. As with everything else, there are probably a few “right” ways to design the URI template, and a few “wrong” ways. A convention that is often used is to simply use the entity/object collection name. Ah (you might say)… it’s the same URI template as the GetSuppliers method – that’s a problem. Well, it won’t be, because we will be decorating the method attribute so that it names the HTTP POST method. So, in summary:

  • A GET to “Suppliers” will return the collection of Supplier objects
  • A POST to “Suppliers” will enable a new Supplier to be added

.

After a successful POST (to add a new Supplier), what should happen? Should we respond to the requestor/consumer in some way? Well, by convention, the object that was added to the collection is returned to the requestor. This acts both as a confirmation that the POST was successful, and enables the requestor to parse/process the response, and learn about data properties that weren’t part of the POST – for example, an auto-generated identity (e.g. SupplierID) for the new object.

.

Coding a POST method

First, the method is decorated with a “WebInvoke” attribute. While the “WebGet” attribute is set for GET methods, “WebInvoke” is intended for POST, PUT, and DELETE methods. Follow the same syntax guideline as “WebGet”, but add the method specifier.

Next, the method’s signature/declaration is important. The return type should be a Supplier object. The name can be anything; in our example, it will be called “NewSupplier”.  The argument must be a Supplier object.

Then, we initialize the object context.

Now, we call the AddObject method on the context entity, and save the changes to the context. Finally, we return the Supplier object that was added. Incidentally, the result of the AddObject and SaveChanges methods adds the auto-generated SupplierID identity, and provides default values for the properties that were not included in the request body.

Our NewSupplier method appears below.

.

What is the format of the request body? Using the information on the service “help” page

We have the NewSupplier method, and it’s ready to use. What is the format of the request body?

For this example, and the remaining examples in this post, we will send and receive XML. (WCF WebHttp Services support JSON out-of-the-box, but we will use XML for now.)

The easiest way to determine the format of the request body is to use the service “help” page. Go ahead, and open it now; it should look similar to the following:

Click/select the Suppliers > POST link. Spend a few moments and study the information on this page. It clearly shows you the request body format:

Therefore, to add a new Supplier, use this format. For testing, you can copy-and-paste. A few things to note:

  • Do not provide properties that are auto-generated by the entity data model; for example, SupplierID
  • If you don’t have a value for a property, you don’t have to include it; the values will be initialized in accordance with the rules in the entity data model
  • Typically, do not include navigation properties and their data (which are usually collections themselves); for example, don’t include “nwProducts” here

.

Now, you should be able to create a request body, and POST it to the Suppliers collection successfully.

.

Coding a DELETE method

We’ll code the DELETE method now, because it’s easy.

The URI template design is important. We are deleting an object from a collection, so we should include the collection as part of the URI template. We then need to identify the specific object to be deleted. It turns out that we can re-use the URI that does a GET for a specific supplier: http://localhost:8080/firstef/suppliers/8

Make sure you specify the DELETE method in the WebInvoke attribute.

Our method will be called DeleteSupplier. It will take an argument – the SupplierID (as a string, obviously). We’ll need to convert the string to an int, in the method body.

As usual now, initialize an object context.

Then, fetch the object that will be deleted. Your production code should detect whether the fetch result is null, and react accordingly. In our example here, we will not include error-checking.

Then call the context entity’s DeleteObject method, and save the context.

Typically, there’s no return value for a DELETE method. However, for testing, you can return a string if you wish confirmation that your code is working.

Our DeleteSupplier method is shown below.

Build and run. It should work correctly.

.

Coding a PUT method

Coding a PUT method will include principles from POST and DELETE. We need to send a request body. We need to fetch a specific object, change it, and save it.

The URI template design should be suggested to you by now, as a result of the experience you’ve accumulated so far, and some thought and inference on your part. We’ll re-use the URI template from the specific Supplier GET or DELETE. This time, the WebInvoke will be a PUT.

Our method will be called UpdateSupplier. It will take an argument – the SupplierID (as a string, obviously). We’ll need to convert the string to an int, in the method body.

As usual now, initialize an object context.

Then, fetch the object that will be updated. Your production code should detect whether the fetch result is null, and react accordingly. In our example here, we will not include error-checking.

Now, we have to give some thought to the kind of update we’ll do – a brute-force, “replace everything” update, or a selective “per property” update.

Reminder: This is an entry-level example of a data access-enabled WCF WebHttp Service. The approach we use here is simple, and helps the new WCF developer gain some experience and success quickly. For more complex scenarios, WCF and the Entity Framework offer more sophisticated and full-featured approaches.

If you choose a “replace everything” update, then you will require (and assume) that the requestor includes a fully-configured object in the request body, with all properties present – the changed properties, and the unchanged properties.

If you choose a “per property” update, then your code will have to check for the presence of each property, and act accordingly: If present, update. If not, keep the original property.

After processing the properties, save the context changes.

For a return type, the convention is to return the object, showing all the  properties (changed and unchanged).

Our DeleteSupplier method is shown below.

Build and run. It should work correctly.

.

Summary

In this post, you learned how to add a data access layer to a WCF WebHttp Service, which supports the four basic HTTP methods – GET, POST, PUT, and DELETE.

While the concepts and techniques were intended for an entry-level audience, you were able to build a foundation, get some early success, and anticipate where you will have to develop additional understanding and skills to handle more complex scenarios.

.

Learning resources

Now that we have worked our way through the task of adding a data access layer to a WCF WebHttp Service, it’s appropriate to present some links to reference and learning resource material that was helpful in the preparation of this blog post. Although each one of these was helpful in some manner, we are confident that the quality of material “out there” will improve over time. Hopefully this blog post helps in some way. Keep in mind that in October 2010, we’re early in the life cycle of WCF WebHttp Services on the .NET Framework 4 – it’s all still pretty new.

.

Warning: The presentation of the following list of links is NOT intended to be authoritative or comprehensive. It is NOT intended to be used sequentially, or as a topic-oriented reference.

Making a data-aware WCF WebHttp Service can grow to be very complex, with many moving parts. Over time, you need to develop a good base of knowledge, and be able to apply the knowledge in a practical manner. Skimming some of these resources will give you an indication of some of the issues that you will face.

.

Resources that were very helpful:

The ADO.Net Entity Framework, POCO Objects and You

Exposing POCO entity from EFDM through WCF to client

Entity Framework 4, WCF & Lazy Loading Tip

(book) Programming Entity Framework, chapters 17 and 18 (probably in the College’s digital books collection)

Walkthrough: Serialize POCO Proxies with WCF

POCO Proxies Part 1 and Part 2

.

Resources that were somewhat helpful:

Introducing WCF WebHttp Services in .NET 4 (index page for a 12-part series, somewhat useful)

Getting Started with WCF WebHttp Services in .NET 4 (part 1)

Walkthrough: POCO Template for the Entity Framework

Sneak Peek – Using Code Generation Templates with the Entity Framework 4.0

Sneak Preview: Persistence Ignorance and POCO in Entity Framework 4.0

Working with Objects

Working with POCO Entities

POCO in the Entity Framework: Part 1 – The Experience

Configuring Tracing (for WCF)

EF4 POCO WCF Serialization problems (no lazy loading, proxy/no proxy, circular references, etc)

Automatic and Explicit Format Selection in WCF WebHttp Services

Cannonical REST Entity Service v1

Also – various videos on Channel 9, and from various Microsoft developer events (PDC etc.)

.


Advertisements
Categories: 2010 Fall DPS907
  1. Eric Christensen
    March 14, 2011 at 10:52 am

    Thanks! I was looking all over for a solution to this. Great post and easy to follow.

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: