SQL Zone is brought to you in partnership with:

sidar has posted 2 posts at DZone. View Full User Profile

Linq to SQL with WCF in a Multi Tiered Environment – Part 1

06.10.2008
| 6645 views |
  • submit to reddit

In many places, forums, blogs, or techy talks with some colleagues I hear some ongoing urban legends about Linq to SQL I came across:

  • You can not implement multi tiered applications with Linq to SQL

  • Linq to SQL can not be used for enterprise level applications

I can’t say that both of these statements are particularly wrong or right, of course Linq to SQL can not handle every scenario but in fairness it handles most of the scenarios sometimes even better than some other RAD oriented ORM s. In this post I will create a simulation of an enterprise web application, having its Data Access, Services, and Presentation Layers separated and let them communicate with each other (err.., at least from service to UI) through WCF – Windows Communication Foundation.

This will be a couple of (may be more) posts, and this is the first part of it. I’ll post the sample code with the next post.

I have to say that this article is neither an introduction to Linq to SQL nor to WCF, so you need basic knowledge of both worlds in order to benefit from this mash up. We will develop an application step by step with an easy scenario but will have the most important characteristics of a disconnected (from DataContext perspective), multi layered enterprise architecture.

Since this architecture is more scalable and reliable, implementing it with Linq to SQL has also some tricks to keep in mind:

  • Our DataContext will be dead most of the time. So we won’t be able to benefit Object Tracking to generate our SQL statements out of the box.

  • This also brings to the table that we have to know what entities to delete, what to insert, and what to update. We can not just “do it” and submit changes as we are doing it in connected mode. This means that we have to maintain the state of the objects manually (sorry folks, I feel the same pain).

  • The transport of the data over the wire is another problem, since we don’t write the entities on our own(and in the case of an amend to them the designer of Linq to SQL can be very aggressive) so it brings us into 2 common situation

  • We can create our own entities, and write translators to convert from Linq Entities to our very own ones.

  • We can try to customize Linq Entities in the ways we are able to.

Since the first one is obvious and the straight forward to implement, we will go down the second route to explore the boundaries of this customization.

To make it clearer that what I will do, here is a basic but a functional schema of the resulting n-tier application:

Picture 1 – Architectural schema of the sample app.Picture 1 – Architectural schema of the sample app.

In our example, we are going to use Linq to SQL as an ORM Mapper. So as you see in the schema, Linq to SQL doesn’t give us the heaven of not writing a DAL Layer at all. But it reduces both stored queries/procedures and amount of mapping that we had to do manually before.

Developing the Application

Scenario

The scenario I came up with is a favorites web site, that consist of 2 simple pages enabling its users to Insert, Delete, Update and Retrieve users and their favorites when requested. 1 user can have many favorites.

We will simply place 2 Grid Views in the page and handle their events to make the necessary modifications on the model itself. This will also demonstrate a common usage.

Design

Entities

Here is the object diagram of the entities; they are the same as the DB tables:

Picture 2.Entity DiagramPicture 2.Entity Diagram

See the additional “Version” fields in the entities; they are type of Binary in .NET and TimeStamps in SQL Server 2005. We will use them to let Linq to SQL handle the concurrency issues for us.

Since we are going to employ a web service by the help of WCF, we need to mark our entities as DataContract to make it available for serialization through DataContractSerializer. We can do that by right clicking on the designer and going to properties, and changing Serialization property to unidirectional as in the picture follows:

Picture 3. Properties windowPicture 3. Properties window

After doing and saving this we will see in the designer.cs file, we have our Entities marked as DataContract and members as DataMember s.

As mentioned earlier before, we need to maintain our entites state – to know whether they are deleted, inserted, or updated. To do this I am going to define an enumeration as follows:

/// <summary>
/// The enum helps to identify what is the latest state of the entity.
/// </summary>

public enum EntityStatus
{

/// <summary>
/// The entity mode is not set.
/// </summary>

None = 0,

/// <summary>
/// The entity is brand new.
/// </summary>

New = 1,

/// <summary>
/// Entity is updated. 
/// </summary>

Updated = 2,

/// <summary>
/// Entity is deleted. 
/// </summary>

Deleted = 3,

}

We are going to have this field in every entity, so let’s define a Base Entity with this field in it:

[DataContract]
public class BaseEntity

{

/// <summary>
/// Gets or sets the status of the entity.
/// </summary>
/// <value>The status.</value>

[DataMember]

public EntityStatus Status { get; set; }

}

And then, all we need to do is to create partial classes for our Entities and extend them from base entity:

public partial class User : BaseEntity

{

}

public partial class Favorite : BaseEntity

{

}

Now our entities are ready to travel safely along with their arsenal.

Service Layer Design

As we are going to use WCF, we need to have our:

  • Service Contracts (Interfaces)
  • Service Implementations (Concrete classes)
  • Service Clients (Consumers)
  • Service Host (Web service in our case)

Service Contracts

We will have 2 services: Favorites Service and Users Service. It will have 4 methods: 2 Gets and 2 Updates. We will do the insertion, update, and deletion depending on the status so there is no need to determine separate functions for all. Here is the contract for User:

 /// <summary>
/// Contract for user operations 
/// </summary>

[ServiceContract]

public interface IUsersService

{

/// <summary>
/// Gets all users.
 /// </summary>
/// <returns></returns>

[OperationContract]

IList<User> GetAllUsers();

/// <summary>
/// Updates the user.
/// </summary>
/// <param name=”user”>The user.</param>

[OperationContract]

void UpdateUser(User user);

/// <summary>
/// Gets the user by id.
/// </summary>
/// <param name=”id”>The id.</param>
/// <returns></returns>

[OperationContract]

User GetUserById(int id);

/// <summary>
/// Updates the users in the list according to their state.
/// </summary>
/// <param name=”updateList”>The update list.</param>

[OperationContract]

void UpdateUsers(IList<User> updateList);

}

And here is the contract for Favorites Service:

/// <summary>
/// Contract for favorites service
/// </summary>

[ServiceContract]

public interface IFavoritesService

{

// <summary>
/// Gets the favorites for user.
/// </summary>
/// <param name=”user”>The user.</param>
/// <returns></returns>

[OperationContract]

IList<Favorite> GetFavoritesForUser(User user);

/// <summary>
/// Updates the favorites for user.
/// </summary>
/// <param name=”user”>The user.</param>

[OperationContract]

void UpdateFavoritesForUser(User user);

}

Service Implementations (Concrete classes)

Since we are developing a db application with no business logic at all, the service layer implementors are pretty lean & mean. Here is the Service implementation for UserService:

[ServiceBehavior(IncludeExceptionDetailInFaults=true)]

public class UsersService : IUsersService

{

IUsersDataAccess DataAccess { get; set; }

public UsersService()

{

DataAccess = new UsersDataAccess();

}

#region IUsersService Members

/// <summary>
/// Gets all users.
/// </summary>
/// <returns></returns>

[OperationBehavior]

public IList<User> GetAllUsers()

{

return DataAccess.GetAllUsers();

}

/// <summary>
/// Updates the user.
/// </summary>
/// <param name=”user”>The user.</param>

[OperationBehavior]

public void UpdateUser(User user)

{

DataAccess.UpdateUser(user);

}

/// <summary>
/// Gets the user by id.
/// </summary>
/// <param name=”id”>The id.</param>
/// <returns></returns>

[OperationBehavior]

public User GetUserById(int id)

{

return DataAccess.GetUserById(id);

}

/// <summary>
/// Updates the users in the list according to their state.
/// </summary>
/// <param name=”updateList”>The update list.</param>

[OperationBehavior]

public void UpdateUsers(IList<User> updateList)

{

DataAccess.UpdateUsers(updateList);

}

#endregion

}

And as you can imagine the favorite service implementation is pretty much the same.

This has been long enough, so let’s cut it here. In the next post, I will talk about the presentation, service and data layer implementations. By that, we will see how to best approach to modifying these entities in a data grid, pass them through the WCF Proxy and commit the changes (insert, update, delete) to the SQL 2005 database. I will also provide the source codes with the next post. Stay tuned until then.

Published at DZone with permission of its author, sidar ok.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Sebastian Jancke replied on Thu, 2008/06/19 - 3:10pm

The whole idea, to use one model for your domain (logic) and service contracts (and sometimes also reporting), seems to me as another joke from microsoft. Only simple and small projects don't get you into trouble with tight coupling and high maintenance costs, etc...

 

-Sebastian

sidar ok replied on Sat, 2008/06/28 - 12:58pm

That's true, if you are looking to a pure DDD, you would want high level of PI and enhanced POCO support. Then with Linq to SQL(and entity framework) you dont have much chances, as I said in the article you need to write your translators ( not mappers) from linq entities to your domain objects - and vice versa. 

Sebastian Jancke replied on Sat, 2008/06/28 - 3:39pm

Agree.

sidar ok replied on Sat, 2008/06/28 - 4:40pm

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.