views:

275

answers:

2

I'm about to show my inexperience here, but hey - like any developer I want to learn.

Given the following interface:

public interface IRepository
{
    entityDB Database { get; set; }

    IQueryable<T> All<T>() where T:class, new();
    T Single<T>(Expression<Func<T, bool>> expression) where T : class, new();
    IList<T> Find<T>(Expression<Func<T, bool>> expression) where T : class, new();
}

How would I go about creating a usable repository class such that I can have the generic type T replaced with a strong type?

Assuming I have a photo portfolio site that has a Photos entity and a CameraSettings entity, how do I define the types - and therefore the L2S - that gets included in my concrete class? Currently when I implement the class, it expects the following form:

public class PhotoRepository : IRepository
{
    public override IQueryable<T> All<T>()
    {
        // code goes here...
    }

    public override T Single<T>(System.Linq.Expressions.Expression<Func<T, bool>> expression)
    {
        // ...and here...
    }

    public override IList<T> Find<T>(System.Linq.Expressions.Expression<Func<T, bool>> expression)
    {
        // ... and let's not forget here.
    }
}

Am I even going about this the right way, or should I be using an interface that looks like this:

public interface IRepository<T> where T:class
{
    entityDB Database { get; set; }

    IQueryable<T> All();
    T Single<T>;
    IList<T> Find();
}

I just want to get my unit tests working correctly and be confident that I'm not learning bad (or just plain ugly) habits. Appreciate the steer in the right direction.

+2  A: 

If I've understand you right you want create separate repositories for every your entity (or just for a few of them). In this case appropriate solution is to use the second version of the interface that you posted. Like this:

public interface IRepository<T> where T : class, new()
{
    entityDB Database { get; set; }

    IQueryable<T> All();
    T Single(Expression<Func<T, bool>> expression);
    IList<T> Find(Expression<Func<T, bool>> expression);
} 

And implementation of PhotoRepository will look like this:

public class PhotoRepository : IRepository<Photo>
{
    public IQueryable<Photo> All()
    {
        // code goes here...
    }

    public Photo Single(Expression<Func<Photo, bool>> expression)
    {
        // ...and here...   
    }

    public IList<Photo> Find(Expression<Func<Photo, bool>> expression)
    {
        // ... and let's not forget here.   
    }
}

Also if your repositories will operate with the Entity Framework entities you can define the IRepository interface more exactly:

public interface IRepository<T> where T : EntityObject, new()
bniwredyc
Thanks. Being new to EF I figured there'd be something more specific that was a better implementation.
Phil.Wheeler
+2  A: 

In my project, I've created my own IRepository:

public interface IRepository<T>
{
    //Retrieves list of items in table
    IQueryable<T> List();
    IQueryable<T> List(params string[] includes);
    //Creates from detached item
    void Create(T item);
    void Delete(int id);
    T Get(int id);
    T Get(int id, params string[] includes);
    void SaveChanges();
}

Here is generic implementation:

public class Repository<T> : IRepository<T> where T : EntityObject
{
    private ObjectContext _ctx;

    public Repository(ObjectContext ctx)
    {
        _ctx = ctx;
    }


    private static string EntitySetName
    {
        get
        {
            return String.Format(@"{0}Set", typeof(T).Name);
        }
    }

    private ObjectQuery<T> ObjectQueryList()
    {
        var list = _ctx.CreateQuery<T>(EntitySetName);
        return list;
    }

    #region IRepository<T> Members

    public IQueryable<T> List()
    {
        return ObjectQueryList().OrderBy("it.ID").AsQueryable();
    }

    public IQueryable<T> List(params string[] includes)
    {
        var list = ObjectQueryList();

        foreach(string include in includes)
        {
            list = list.Include(include);
        }

        return list;
    }

    public void Create(T item)
    {
        _ctx.AddObject(EntitySetName, item);
    }

    public void Delete(int id)
    {
        var item = Get(id);
        _ctx.DeleteObject(item);
    }

    public T Get(int id)
    {
        var list = ObjectQueryList();
        return list.Where("ID = @0", id).First();
    }

    public T Get(int id, params string[] includes)
    {
        var list = List(includes);
        return list.Where("ID = @0", id).First();
    }

    public void SaveChanges()
    {
        _ctx.SaveChanges();
    }

    #endregion

}

If you want simple repository, you just use Repository<ClassName>. If you want to enhance it, you can define ClassNameRepository : Repository<ClassName> and write another methods. You don't have to define List() method in every repository, because it is done in base. Before writing my repository I made assumptions:

  • Every primary key field in my database is called ID.
  • Every EntitySetName is constructed from class name and Set. For User class it will be UserSet.

This solution uses Dynamic LINQ:

http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

It works pretty well.

LukLed
I like this. It looks clean and pretty reusable. Might have to give it a go. Cheers.
Phil.Wheeler