views:

4042

answers:

3

I feel like I'm running around in circles. I can't seem to make up my mind as to what the right Repository pattern is using Linq To Sql. If you're familiar with Rob Conery's MVC Storefront you will see that his implementation wraps the Linq-Generated models with another class and treats the Linq-Generated one simply as a Data Transfer Object. It looks something like this:

//Custom wrapper class.
namespace Data
{
    public class Customer
    {
         public int Id {get;set;}
         public string Name {get;set;}
         public IList<Address> Addresses {get;set;}
    }
}

//Linq-Generated Class - severly abbreviated
namespace SqlRepository
{
    public class Customer
    {
         public int Id {get;set;}
         public string Name {get;set;}
         public EntitySet<Address> {get;set;}
    }
}

//Customer Repository
namespace SqlRepository
{
    public class UserRepository : IUserRepository
    {
        private _db = new DB(); //This is the Linq-To-Sql datacontext

        public IQueryable GetCusomters()
        {
            return from c in _db.Customers
                   select new Customer // This is the wrapper class not the gen'd one
                   {
                      Id = c.Id,
                      Name = c.Name,
                      Addresses = new LazyList(c.Addresses)
                   };
        }

What is the advantage of doing it this way (using a wrapper class), as opposed to the way Mike Hadlow suggests here in his version of IRepository where he just returns the DTO objects from the repository?

My other question is, where should business logic be enforced and checked? Is this in a seperate layer all together called by the repository on save/update, or is it built-in to the wrapper class?

Thanks for all the help.

+8  A: 

It depends on where the DTOs are defined and how you want to test it. If you use DBML, then LINQ-to-SQL wants to generate the data objects in the data layer. While LINQ-to-SQL supports persistance ignorance, it doesn't go out of its way to make it easy. EF doesn't support it at all.

This means that in the standard model, your data layer is defining all the domain entities, which is tricky if you want to test the UI/business layers in true isolation from the data layer.

A pragmatic approach might be to use the data object definitions from the data layer in unit tests, but not the data-context (i.e. hide the data-context behind the repository interface, but expose the entity types) - but this is muddying the waters a little, and means your UI etc need to strongly reference the data layer. But if you think of this as a "domain model layer which happens to also contain a repository implementation that we may or may not be using" you might justify it.

Keeping completely separate domain entities makes unit testing and IoC more "pure", but increases the amount of code you have (so double-edged).

Marc Gravell
+1  A: 

Are the gen'd objects serializable? I had the impression they weren't. It's just a case of isolation as Marc Gravel said above..

What if you switch the repository and have a MySQL/Oracle/XML files/Web service/whatever data provider (Repository)? YOu would be tied to the LINQ to SQL assembly to reference the entities, right? Which, ofcourse, you woldn't want..

Andrei Rinea
You can make them serializable as an option.
Micah
+24  A: 

The thing is this, Linq to SQL is not a true ORM, it is a data access layer generator. You can make it be an ORM by going deep by hand editing XML files and playing with sqlmetal and whatnot, but where it shines is as a DAL.

The idea behind an Object Relation Mapper is this, you have your sql database, and your domain objects. To design a database properly, you are going to do things (like normalization) that logically don't translate into a properly designed object model, and vice-versa. This is called "Impedance Mismatch", the role of an ORM is to deal with that mismatch in a clean, effective, and efficient way. The less painful database interaction is almost a secondary thing.

The idea behind a repository is that it encapsulates all persistence logic and dependencies on infrastructure from the rest of your application. When your application needs a Customer object, it shouldn't have to know whether it is coming from SQL Server, MySQL, an XML file, or ASP.net Membership. Once you have that de-coupling, any changes you make to your persistence story have no effect on the rest of your application.

With that in mind, it gets more clear why he did what he did. L2S is used to generate the DAL, but the only thing that should know about the DAL is the repository, so a translation is made to his domain objects. That way he can refactor his domain model without worrying about his persistence story, and he can refactor his database without worrying about rippling effects through his application. He could also start coding on business logic before deciding on questions like what ORM to use, or even where to store his data.

If he were to use a real ORM (like NHibernate), that mapping code gets handled elsewhere (either in XML or bootstrapping classes). I think L2S (and Robs open source DAL, SubSonic) are great projects, but more designed for smaller, 2-tier apps where something like the repository pattern is over kill. The storefront is also a good illustration of why the additional complexity of NHibernate can be important, he could have saved himself a lot of code by going with something built to handle that sort of scenario, rather then doing it all manually.

Matt Briggs
but what do you do with linq to sql extension methods that youve used in your repository once you switch to something like NHIBERNATE? create a new repository?
zsharp
This is a great explanation. The problem I think a lot of people have (read "I") is trying to treat L2S objects as something other than solely a DAL. But isn't part of that MS's fault for building all kinds of hooks for validation etc.?
Micah
There is nothing wrong with a 2-tier architecture, if that is all that is needed. In that case, L2S is a great product, and the model objects it generates are very customizable and extensible. The problem is scaling up to an n-tier pattern, I feel a project like L2S starts to break down
Matt Briggs