views:

99

answers:

3

I've been using Linq to SQL for a new implementation that I've been working on. I have about 5000 lines of code and am a little ways from a solid demo. I've been pretty satisfied with Linq to SQL so far -- the tools are excellent and pretty painless and it allows you to get a DAL up and running quickly.

That said, there are some major draw backs that I just keep hitting over and over again. Namely how to handle separation of concerns between my DAL and my business layer and juggling that with different data contexts.

Here is the architecture I've been using: My repositories do all my data access and they return Linq to SQL objects. Each of my Linq to SQL objects implements an IDetachable interface. A typical implementation looks like this:

partial class PaymentDetail : IDetachable
{
    #region IDetachable Members

    public bool IsAttached
    {
        get { return PropertyChanging != null; }
    }

    public void Detach()
    {
        if (IsAttached)
        {
            PropertyChanged = null;
            PropertyChanging = null;

            Transaction.Detach();
        }
    }

    #endregion
}

Every time I do a DAL operation in my repository I "detach" when I'm done with the object (and it should theoretically detach from any child objects) to remove the DataContext's context.

Like I said, this works pretty well, but there are some edge cases that seem to be a big pain in the ass. For instance, my Transaction object has many PaymentDetails. Even when there are no PaymentDetails in that collection it's still attached to the DataContext's context! Thus, if I try to update (I update by Attach()ing to the object and then SubmitChanges()) I get that dreaded "An attempt has been made to Attach or Add an entity that is not new, perhaps having been loaded from another DataContext. This is not supported." message.

Anyway, I'm starting to doubt that this technology was a good gamble. Has anyone got a decent architecture that they're willing to share? I'd really love to use this technology but I feel like I spend 1/3 of my time just debugging is retarded quirks!

+1  A: 

The lifetime of a DataContext should be the lifetime of your "Unit of Work."

Example: Moving money from one account to another

This involves two steps, in an atomic transaction:

  1. Add money to the target account
  2. Subtract money from the source account

Your DataContext should not detach itself after step one. It should stay alive until both steps are completed.

Robert Harvey
But this doesn't really make sense in a more "engineered" environment, does it? i.e. multiple objects, each with references to each other, how can you possible apply the model you describe with a repository for transactions, a repository for customers, etc.?
Brad Heller
Rick Strahl has written up a pretty good examinations of the issues here: http://www.west-wind.com/weblog/posts/246222.aspx
Robert Harvey
@Brad: If you are working with multiple repositories, they all need to work from the same data context object behind the scenes. Generally speaking, applications using repository style abstractions and working with multiple repositories at once will have an IUnitOfWork abstraction to bind the repositories together.
OdeToCode
A: 

If you're looking for an example, try StackOverflow!

For a peek into StackOverflow's LINQ To SQL model, check out this video from PDC 2008 at around 51:00.

p.campbell
+1  A: 

I personally use DataContext only within the "using" blocks for all CRUD methods.

There is a simpler way to use Detach(). I use it only when I save an entity, updates work fine. There are few articles online on how to "Fix" that dreaded error that you have mentioned, but from my experience, when the DAL is properly designed you don't need to do that at all.

I also set the deferred loading so that it doesn't load any associated data and I work with one entity at a time, i.e. Order, Customer, Item.

When I save the order for multiple items I simple iterate through Items and call .Save() on each item method. Save method decides whether it's an updated or insert based on the Id of the object.

Also using timestamps for concurrency seems to be way easier and more efficient than comparing old and new objects.

vikp