Syndicate

News

My name's Marco De Sanctis and I'm an IT professional from Italy. This is my technical blog, about .NET and related application development and design technologies.

Download my Resume (.doc)

Recent Comments

6/26/2008 at 12:21 AM

I really hope you could help me out on this. Say I...
by Roy

Read more...

6/14/2008 at 4:14 PM

Yep, it happened to me too. It violates the princi...
by Gian Maria

Read more...

6/13/2008 at 5:32 AM

Thank you very much, your postinge.on the coattail...
by cindex

Read more...

6/12/2008 at 1:06 PM

Okay. Maybe sometime I'll try to make my own, inhe...
by Lyr

Read more...

6/11/2008 at 4:09 PM

Hello Lyr,ImageSourceConverter is a TypeConverter ...
by Marco De Sanctis

Read more...

Recent Posts

Unleash the power of VisualStateManager with custom states

6/30/2008 at 12:12 AM

Read more...

Blend 2.5 June CTP and custom controls templating

6/25/2008 at 10:18 AM

Read more...

TransactionScope timeout

6/14/2008 at 12:01 AM

Read more...

Custom controls and default template in Silverlight 2 Beta 2

6/11/2008 at 11:37 AM

Read more...

Silverlight 2 Beta 2 Documentation Available

6/6/2008 at 10:17 AM

Read more...

Linq To Sql in real word Web applications

posted on Tuesday, May 20, 2008 11:34 PM | Filed Under [ LINQ ASP.NET ]

As my Italian friends probably know, I'm a huge NHibernate fan, as I decided to permanently adopt it in many desktop and web apps I've built or designed during the last 2-3 years; dealing with Session life cycle management in ASP.NET apps is pretty simple if you use well known patterns like

  • Session per Request
  • more rarely, Session per Conversation

In other words, you have a session alive and kickin' for the whole business transaction your WebForm is accomplishing and, being the NHibernate Session a Unit of Work implementation, this is absolutely faithful to that pattern definition.

For some reasons, using a similar approach with Linq To Sql's DataContext is not as easy. Let's take a look.

Dealing with detached objects

Let's start saying that associations in L2S are lazy fetched by default, so you need to have a context alive while navigating the entity graph if you don't want ObjectDisposedExceptions to be raised:

image

When adopting a Session per Request pattern, you must frequently detach/reattach the entity versus the current request's DataContext; however, attaching a detached entity in Linq To Sql is not as easy as it's supposed to be, as it isn't a supported scenario. I blogged in Italian a while ago about some strange reattaching behaviors I experienced in Beta2; RTM throws an exception if you try to reattach a persistent entity with associations not yet initialized, at least that's a more consistent behavior, although it doesn't help.

image

In other words, unless you don't want to deal with workarounds, you can't use Session per Request pattern with Linq To Sql.

Moving towards Session DataContext per Conversation

That brings us to the decision of having the same DataContext alive for the whole business transaction. I'm fine with that, although the architecture becomes a bit more complicated because its life should span several postbacks. After chatting a while with my friend Alk, I realized that ASP.NET's Cache is an awesome storage mechanism for DataContexts because:

  • In a perfect world, every business transaction should be explicitely closed. In real world, however, users tend to close their browsers without notice. If we use sliding expiration, the ASP.NET's caching infrastructure takes care itself on getting rid of every pending DataContext that hasn't properly been disposed.
  • Cache's sliding expiration is automatically renewed on every access.
  • Cache provides a callback mechanism, so we can execute custom code when a DataContext has expired.
  • We can configure Cache to not remove DataContexts when the system is in need of memory.

Instead of directly storing the DataContext, it's a better choice to wrap it in a generic UnitOfWork instance; this allows us to inject a bit of additional (and useful) logic:

The Items property, for example, is a string/object dictionary (an idea borrowed from NHibernate.Burrow) used to store custom transaction items that need to last till the transaction ends; I've also provided a Disposing event if the user wants to execute some custom code when a UnitOfWork is closed.

A UnitOfWorkContainer singleton object keeps track of all active UnitOfWorks and provides methods to start, rollback or commit (business) transactions:

image

Thanks to that, building a new UnitOfWork and storing it in the cache repository is only a matter of calling the BeginTransaction<T> (T is the Linq to Sql context's concrete type) of the UnitOfWorkContainer singleton class:

image

That method returns an ID which can be easily stored (f.e. using Viewstate) and will be used to retrieve the current UoW during the future postbacks. Now that a UoW has been created and stored into the container, we can retrieve it very easily

image

and we can use it to interact with the underlying Linq DataContext or with the Items storage as well; for example, the Page_Load method in my example contains the following code:

image

Thanks to this code, in every postback we have a reference to the person currently edited through the entity filed, we can freely navigate its object graph with the DataContext automatically querying the underlying database if it needs to lazily initialize any property, and tracking every change we make to any entity, being it the current Person instance or any associated one. Not bad, isn't it?

After some postbacks, here it comes the end of the conversation, in which the user is asked to say "Ok" and persist the changes, or say "No" and cancel everything; this ends the business transaction and invalidates the UnitOfWork:

image

Few details on what happens behind the curtain

The UnitOfWorkContainer class acts as a repository, providing methods to manage a UoW's lifetime and retrieve it given its Id. Once the user decides to begin a new business transaction, a new UoW is created and added to ASP.NET Cache:

image

Two details to notice here:

  1. The cache item is marked as NotRemovable, so it won't be cleared in case of memory shortage; it will disappear only after a 10 minutes sliding timeout or if explicitely removed.
  2. I've subscribed a CacheItemRemovedCallback to dispose the UoW once it has been removed from the cache (and, therefore, not reachable anymore from the client code)

Obviously this is only a very simple example, something very similar to what I used to do with NHibernate. For those who are interested on it as a starting point for their own apps, source code is available here.

kick it on DotNetKicks.com

Comments

Gravatar
# re: Linq To Sql in real word Web applications
Posted by Egil on 5/21/2008 11:40 PM
I like this.

A few comments though:

I have always had the impression that the DataContext was suppose to be as short lived as possible. This pattern sort of goes against that.

Also, traversing the object graph is freely seems like a wast of resources - a lot of round trips to the SQL server. I think the recommended method is to use LoadWith method. I guess it is a balancing act between convenience and performance.

Another thing that hit me. If two users is working with the same set of data, there will surly be two active DataContexts alive, but are the DataContexts intelligent enough to share the data object between them, or will there be duplicated items all over?

I am about to start building a internal web application in my company, and I am tempted to forgo all the glory of object tracking and just use Linq to SQL just to retrieve data.

Thanks for an inspiring post, hope my comment are not to negative, it is basically a recap of the debate I have been having with my self lately.

Regards, Egil.
Gravatar
# re: Linq To Sql in real word Web applications
Posted by Maarten on 5/22/2008 8:49 AM
I've tried pretty much the exact same thing, and I could get it to work with a new DataContext on the PostBack. I also used the Attach-method, but used the overload where I also give the original object as an argument. I saved the original object (and the changing one btw) in the ViewState of the page.

I don't have the code here :-(

Cheers,
Maarten
Gravatar
# re: Linq To Sql in real word Web applications
Posted by Marco De Sanctis on 5/22/2008 12:48 PM
@Egil
Your feedback lead me to that post
http://www.codemetropolis.com/archive/2008/05/22/when-lazy-loading-associations-is-useful.aspx
about when lazy loading is useful.

However, having a DataContext alive is essential if you're using its object tracking feature. If you're not (as you wrote :D) and you're sure you're not going to need any additional fetch, you can deal with its lack.
However, consider that having a DataContext alive for the shortest time possible isn't such a big deal in terms of optimization: it simply disposes the internal connection, which is automatically closed after every db access.

@Maarten
Attaching a not completely initialized entity (i.e. with relationships to other entities not yet loaded) isn't a supported scenario and doesn't work in L2S AFAIK.
Gravatar
# re: Linq To Sql in real word Web applications
Posted by Egil on 5/22/2008 3:42 PM
@Marco

Thanks for the feedback.

How would the caching strategy be when using the Unite of Work model? If each user requests pretty much the same subset of date, would that have to be retrieved from the SQL server once per UnitOfWork, or is there a way to cache the data such that it can be reused by each UnitOfWork and only exists once in memory?

Gravatar
# re: Linq To Sql in real word Web applications
Posted by Marco De Sanctis on 5/23/2008 2:01 AM
Hi Egil, sorry for replying so late but I really didn't have time.

Well, integrating ASP.NET cache with Linq To Sql is a bit tricky, because the ORM doesn't provide some kinda of an infrastructure to do it (as, f.e. NHibernate does in an excellent way).

However, a simple example could be about many-to-one associations, leveraging the identity map.

Let's suppose you have a Player entity with a Role property (of type Role): Point Guard, Small Forward, and so on... Roles can be considerent invariant throughout the whole application life, so thery're good candidates to be cached.

What you can do in a PlayerList form, is retrieving all the Roles from the cache and Attaching them to the current UnitOfWork.DataContext. You can do it as they are completely initialized.

Fair enough. Now, when accessing every player's Role property, L2S won't fetch it from the DB: that Role is for sure already stored into the internal Table<Role> and the identity map will use that instance when lazy loading and no query will be executed.
Gravatar
# re: Linq To Sql in real word Web applications
Posted by Egil on 5/23/2008 10:54 AM
No problem Marco, interesting idea though.

I have been playing around with a few ideas on how to make the UOW pattern fit in to a classic layered application, i.e. a facade that the webforms talk to, a business layer below that, and again a data access layer below that.

It seems to me that the UOW would have to be on the BLL, and have the facade expose methods for manipulating the UOW's.

Alternatively, the UOW should be on top of the facade and wrap that instead of a DataContext, since the UOW pattern really only seems relevant in web scenario.

I would love your thought on this.

ps. please consider making the comment box bigger, seven small lines makes it hard to for a general view of ones post.
Gravatar
# re: Linq To Sql in real word Web applications
Posted by Marco De Sanctis on 5/23/2008 4:17 PM
Well,

I see better a solution in which a Facade wraps the UoW and integrates f.e. the cache functionalities: as you correctly say, it belongs to the BL and shouldn't be aware of any ASP.NET related infrastructure.

Actually I don't completely agree with you about the UoW being relevant only on a web scenario; the concepts I wrote in this post IMHO perfectly fit in a smart client environment too, although you don't need all the infrastructure to store it: UoW is something able to track objects status within a business transaction, and that is exactly what a DataContext does. Maybe you don't need to encapsulate it in a UnitOfWork wrapper, but you still need something capable to track object changes and an identity map to sync object identity and database identity, especially if you're going to need a business transaction that spans over more than one single form.

BTW, thank you for the tip about the comment box: i'll fix it ASAP, I actually had to write this comment on notepad!! :-)

Bye,
Marco
Gravatar
# re: Linq To Sql in real word Web applications
Posted by Egil on 5/23/2008 4:45 PM
Ahh ok. So if the Facade wraps the UoW, the Facade would basically take over the responsibility UnitOfWorkContainer class.

The Facade would however have to be aware of the ASP.NET infrastructure for this to work, but I guess that is acceptable.
Gravatar
# re: Linq To Sql in real word Web applications
Posted by Daniel on 5/29/2008 7:58 PM
This is an interesting idea. For the applications I've created with L2S, I stuck with a DataContext scoped to the HttpRequest, and played back changes on that with a delegate so that I can just use lambdas on the client to manipulate entity objects: http://www.dimebrain.com/2007/12/using-linq-to-s.html
Post Comment
Title *
Name *
Email
Url
Comment *  
Please add 6 and 4 and type the answer here: