views:

82

answers:

1

For the last 4 years I have been working as an ASP.NET developer in a small company where all applications are built using the Smart UI anti-pattern in .NET 2.0. This means I have no experience with .NET 3.5 and things like LINQ and general concepts like repository and service layers. I realize that in order to find a new job, I need to upgrade my knowledge so I have started reading books, blogs and a lot of SO questions like this one and the time has come to try and make a simple application with what I should have learned.

I want to build a small application to manage bugs in projects.

This is the very basic database diagram I've come up with:

The database

I have translated this diagram to the following classes (I have omitted the Linq to SQL attributes):

class Project
{
    public int ID { get; internal set; }
    public string Name { get; set; }
    public string Description { get; set; }

    private EntitySet<Bug> bugs;
    public EntitySet<Bug> Bugs
    {
        get { return this.bugs; }
        set { this.bugs.Assign(value); }
    }
}

class Bug
{
    public int ID { get; internal set; }
    public string Summary { get; set; }
    public string Description { get; set; }

    private EntityRef<Project> belongsTo;
    public Project BelongsTo
    {
        get { return this.belongsTo.Entity; }
        set { this.belongsTo.Entity = value; }
    }

    private EntityRef<Person> currentStatusSetBy;
    public Person CurrentStatusSetBy
    {
        get { return this.currentStatusSetBy.Entity; }
        set { this.currentStatusSetBy.Entity = value; }
    }

    public Datetime CurrentStatusSetOn { get; set; }
    public BugStatus CurrentStatus { get; set; }

    private EntitySet<BugStatusHistory> previousStatuses
    public EntitySet<BugStatusHistory> PreviousStatuses
    {
        get { return this.previousStatuses; }
        set { this.previousStatuses.Assign(value); }
    }
}

class BugStatusHistory
{
    public int ID { get; internal set; }
    public DateTime StatusSetAt { get; set; }   
    public BugStatus Status { get; set; }

    private EntityRef<Person> statusSetBy;
    public Person StatusSetBy
    {
        get { return this.statusSetBy.Entity; }
        set { this.statusSetBy.Entity = value; }
    }
}

class Person
{
    public ID { get; internal set; }
    public string Name {get; set; }
    public string Email { get; set; }
    public string Login { get; set; }
    public string Password { get; set; }
}

enum BugStatus { New, Confirmed, Solved }

I have put these classes in a class library called DomainModel and I want to reference that DomainModel from an ASP.NET MVC 2 application. From what I have read, I should also add repositories and maybe even a service layer to my DomainModel. This is what is confusing me.

I have read that you shouldn't create a repository for each class/table, but that you should create a repository for aggregates (groups of classes). If an entity can't exist out of the context of another entity, it shouldn't have its own repository. In my example, a Bug is always linked to a Project. Does that mean I should create a repository for the aggregate Project-Bug? What if I want to render a list of all bugs, no matter in what project they are. Should I add a method GetAllBugs() to my IProjectsRepository? Or should I create a separate IBugsRepository for that usage?

I think creating separate repositories could have its advantages here. From what I've read about Linq to SQL, you can set a property on the DataContext to specify how to handle lazy and eager loading. Now, when I get a list of Projects or a single Project, I want to eagerly load the list of Bugs. But I don't want to eagerly load the Project of each Bug in that list. But, if I want to load a list of all Bugs (no matter the project) or a single Bug, I do want to eagerly load the Project, but in this case I don't want to eagerly load the list of Bugs in that Project. My Linq to SQL knowlegde is very limited, but isn't this something that can only be achieved by setting the DataContext properties? Wouldn't that require me to have a DataContext for Projects and a DataContext for Bugs and thus two repositories? Unless it somehow possible to tell the DataContext to eagerly load up to 2 levels and do lazy loading for anything that's deeper? Or is all of this irrelevant because of deferred execution?

Please excuse me for my long, long question (that maybe isn't even that clear) but all this new information is really confusing me.

(I case you like to comment on my database diagram / class structure, please don't spare me :-))

+2  A: 

I personally only use repository patterns with L2S if I will have alternative methods of obtaining the data stored within the database - which typically means that I also have a set of interfaces for Project, Bug etc.

The repository pattern is a ball-ache with L2S because it complicates Updates - you pass out an object from the repository (which opened and closed a DataContext to get it - they should be short-lived you see), you modify it's properties and send it back in for an update - but you can't, because you need the original DataContext. You need to go an get the object again from a new data context, copy the modified values, and update the second one.

To avoid this, you have to start passing in the data context into your repository for most operations (letting the caller determine the lifetime of the data context); which bloats all your method signatures and makes for an all-round pain in the a*s.

The beauty of L2S, for me, is it's speed - so I shudder when I see people needlessly wrapping the DC for the sake of it.

Don't get me wrong - I have a system I'm writing at the moment where every object and 'data service' is abstracted away behind interfaces; and some of those data services are implemented using Linq To Sql; but that's because the whole system is intended to be a hot-swappable back-end for a whole platform. Using L2S has been tricky, but it was still doable.

Secondly - in terms of your concerns about eager and lazy loading - you will typically control this with the DataLoadOptions class and the 'LoadOptions' member of the DC instance.

If you find that you need fine-grained control over which stuff to lazy and eager load, then you don't need separate DCs - you just offload the creation of the datacontext to different methods tailored for the different needs:

public MyDataContext GetDCForProjects()
{
  var DC = new MyDataContext();
  DataLoadOptions dlo = new DataLoadOptions();
  dlo.LoadWith<Project>(p => p.Bugs);
  DC.LoadOptions = dlo;
  return DC;
}

//add more methods for the different needs
Andras Zoltan