Как реализовать шаблон репозитория с интерфейсом, базой и бетоном - PullRequest
1 голос
/ 22 мая 2009

Я почти завершил реализацию своего шаблона хранилища, имея интерфейс IRepository<T>, класс NewsRepository и сущность News. Проблема, с которой я столкнулся, заключалась в попытке абстрагирования общих методов в базовый класс репозитория.

Я не смог найти способ абстрагировать метод Get в NewsRepository, поскольку он содержит конкретное выражение Linq.

Мои вопросы :

1) Как мне абстрагироваться от базового класса методом public T Get(int id), пожалуйста? Единственный способ, которым я сделал это до сих пор, это передать Expression<Func<T,bool>> вместо int, но тогда это де-факто не абстрагирует общее поведение, поскольку каждому подклассу все равно нужно будет передать выражение, которое почти идентично каждый случай, т.е. n => n.id == id.

2) Как передать в базовый класс метода Update подкласс GetViolations и методы map, пожалуйста? Я полагаю, что решение, возможно, заключается в использовании делегатов, но я не смог получить синтаксис для компиляции

Это упрощенный набор кода - на практике у меня есть метод Save, который выполняет обновление и вставку, а не только обновление, показанное здесь.

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();
    }
}

Ответы [ 2 ]

2 голосов
/ 26 мая 2009
  1. L2S не поддерживает ни супертипы слоев, ни использование элементов интерфейса в запросах, что делает повторное использование довольно сложным. Одним из вариантов является динамическое построение дерева выражений. Это немного грязно, но если вы изолируете его от своего репозитория базового класса, это не так уж и плохо.

Вот пример:

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}
            );
    }
}

См. Также http://msdn.microsoft.com/en-us/library/bb397951.aspx

1 голос
/ 22 мая 2009
  1. Это действительно помогает иметь супертип слоя для сущностей . Они поделятся идентификатором собственности. Вам не придется иметь дело с выражением для представления идентификатора proeprty, вы просто будете знать, что это такое.

  2. Шаблон шаблона метода . В этом шаблоне базовое обновление выполняет всю работу, вызывая вспомогательные методы по порядку, а ваши производные классы реализуют эти защищенные абстрактные вспомогательные методы.

...