views:

506

answers:

2

I have almost completed implementing my repository pattern by having a IRepository<T> interface, a NewsRepository class and a News entity. The problem I ran into was trying to abstract out common methods to a base Repository class.

I could not find a way to abstract the Get method in the NewsRepository as it contains a specific Linq expression.

My questions are:

1) How do I abstract to a base class the public T Get(int id) method please? The only way I have done it so far is by passing in Expression<Func<T,bool>> instead of an int, but then that deosn't really abstract out common behaviour as each sub-class will still need to pass in an expression that is almost identical in each case ie n => n.id == id.

2) How do I pass into the base class on the Update method the sub-class GetViolations and map methods please? I imagine the solution is possibly by using delegates, but I couldn't get the syntax to compile

This is a simplified set of the code - in practice I have a Save method which does Update and Insert rather than just the Update shown here.

Thanks for any help.

public interface IRepository<T>
{
    T Get(int id);
    void Update(T item);
}

public class NewsRepository : IRepository<News>
{
    private Table<News> _newsTable;
    public NewsRepository(string connectionString)
    {
        _newsTable = new DataContext(connectionString).GetTable<News>();
    }

    public News Get(int id)
    {
        return _newsTable.SingleOrDefault(n => n.NewsId == id);
    }

    public void Update(News item)
    {
        var errors = item.GetRuleViolations();
        if (errors.Count > 0)
            throw new RuleException(errors);

        News dbNews = _newsTable.SingleOrDefault(n => n.NewsId == item.NewsId);
        map(dbNews, item);

        _newsTable.Context.SubmitChanges();
    }

    private void map(News dbNews, News news)
    {
        dbNews.Title = news.Title;
        dbNews.Article = news.Article;
    }
}

public class Repository<T> where T : class
{
    protected Table<T> _table;

    public Repository(Table<T> t)
    {
        _table = t;
    }

    //How do i do this??! - This doesn't compile due to T no having a NewsId
    public T Get(int id)
    {
    return _table.SingleOrDefault(n => n.NewsId == id);
    }

    //This seems to be a solution, but it's not really abstracting common behaviour as each
    //sub-class will still need to pass in the same linq expression...
    public T Get(Expression<Func<T,bool>> ex)
    {
        return _table.SingleOrDefault(ex);
    }

    public void Update(T item)
    {
        //How is it possible to pass in the GetRuleViolations and map functions to this method?
        var errors = item.GetRuleViolations();
        if (errors.Count > 0)
            throw new RuleException(errors);

        T dbNews = _table.SingleOrDefault(n => n.NewsId == item.NewsId);
        map(dbNews, item);

        _table.Context.SubmitChanges();
    }
}
A: 
  1. It really helps to have a layer supertype for entities. They will share an Id property. You won't have to deal with an expression to represent the id proeprty, you'll just know what it is.

  2. The template method pattern. In this pattern your base Update does all the work calling helper methods in order, and your derived classes implement those protected abstract helper methods.

Matt Hinze
A: 
  1. L2S supports neither layer supertypes nor using interface members in queries, which makes reuse quite difficult. One option is to dynamically build an expression tree. It's a bit messy, but if you isolate it to your base class repository it's not that bad.

Here is an example:

public interface IEntity
{
    int Id { get; }
}

public partial class News : IEntity
{
}

public class Repository<T> where T : class, IEntity
{

    private readonly DataContext _db;

    public Repository(DataContext db)
    {
     _db = db;
    }

    public T Get(int id)
    {
     Expression<Func<T, bool>> hasId = HasId(id);
     return _db.GetTable<T>().Single(hasId);
    }

    // entity => entity.Id == id;   
    private Expression<Func<T, bool>> HasId(int id)
    {
     ParameterExpression entityParameter = Expression.Parameter(typeof (T), "entity");
     return Expression.Lambda<Func<T, bool>>(
      Expression.Equal(
       Expression.Property(entityParameter, "Id"),
       Expression.Constant(id)
       ),
      new[] {entityParameter}
      );
    }
}

See also http://msdn.microsoft.com/en-us/library/bb397951.aspx

Akselsson