views:

293

answers:

1

Hi everyone,

Trying to use Linq to SQL for a small project I'm working on at home. I have generated a the context code and all my entity classes using dbmetal.exe (from the DBLinq project) against a local MySQL database.

Everything is working great, but I'm trying to abstract some redundant code and I'm running into issues trying to do so.

Basically all my entities are of type Table in my context class. For example I have Table and Table.

While I was architecting the various interfaces for the repository behaviors I realized that there some methods that were very redundant with each entity. For example the ID field:

User findById(int id);
Calendar findById(int id);

I designed my tables so they all have 3 fields in common [ID, DATECREATED, DATEUPDATED]. Since those fields are common I wanted to have a common behavior instead of rewriting those methods for each entity.

So I made my repository classes (UserRepository, CalendarRepository) inherit a common "Repository" class that's defined like that:

public class Repository<T> : IDisposable, IRepository<T> where T : class
{
    protected MyContext context;

    private DbLinq.Data.Linq.Table<T> currentTable;

    protected Repository() {
        context = new MyContext();

        Type currentType = this.GetType().GetGenericArguments()[0];
        currentTable = //Set currentTable based on currentType. e.g.: currentTable = context.User;
    }        

    #region IRepository<T> Members

    public T findById(int? id)
    {
        return currentTable.SingleOrDefault(d => d.ID == id);
    }

    public T findByDateCreated(DateTime dateCreated)
    {
        return currentTable.SingleOrDefault(d => DateTime.Equals(dateCreated, d.DateCreated));
    }

    public T findByDateUpdated(DateTime dateUpdated)
    {
        return currentTable.SingleOrDefault(d => DateTime.Equals(dateUpdated, d.DateUpdated));
    }

    public T insert(T domainObject)
    {
        currentTable.InsertOnSubmit(domainObject);
        return domainObject;
    }

    public T save(T domainObject)
    {
        context.SubmitChanges();
        return domainObject;
    }

    #endregion

    #region IDisposable Members

    public void Dispose()
    {
        if (context != null)
            context.Dispose();
    }

    #endregion
}

Well it turns out this is more difficult than I thought. When I try to set:

currentTable = (Table<T>)context.User;

I get the following error:

Cannot convert type 'DbLinq.Data.Linq.Table<Models.Domain.User>' to 'DbLinq.Data.Linq.Table<T>'

Implicit casting does not work either.

Anyone has ever done something similar successfully? That'd be very sad if I had to have all my Repository classes implement the same findById method with exactly the same code in it... I'm sure there is a way to NOT do that, I just can't find it. :)

+2  A: 

That'd be very sad if I had to have all my Repository classes implement the same findById method with exactly the same code in it

The code isn't exactly the same. In each case, you are referring to a different table in your context.

I understand your logic; you want to be DRY. But the reason you have a repository is to give you the capability to abstract your data access logic away by injecting a mock repository instead of your real repository to facilitate unit testing. The most practical (and flexible) way to do this is to keep your tables (and your DAL objects) separate and distinct. By attempting to genericize part of your DAL objects, you are introducing additional coupling which will complicate your unit testing.

Robert Harvey
True the referenced objects are different but the code is identical. But I do get the Unit testing perspective... IOC was actually one of my next step using Ninject. Thanks for your help Robert.
Lancelot