Включает в (полу) общий репозиторий - PullRequest
0 голосов
/ 27 апреля 2011

Я пытаюсь выяснить, как использовать новый материал EF Code First, и у меня возникают проблемы с выяснением, как адаптировать функциональность Include в полуобобщенный класс Repository. (Я говорю полуграничный, потому что класс не универсальный, а только методы. Поэтому у меня есть один репозиторий, который по существу обертывает агрегат, и вы можете взаимодействовать со всеми сущностями, которые являются частью этого агрегата, а не только с одним. )

В моей ситуации у меня есть сущность, у которой есть один дочерний элемент, который всегда должен быть загружен, а затем другие дочерние элементы, которые загружаются только по желанию, я хочу включить простой параметр bool в свой метод репозитория. Вот так:

public S GetByID<S>(int entityID, bool loadChildren = false) where S : class
{
  DbSet<S> set = _context.Set<S>();

  if (loadChildren)
    set = FullInclude<S>(set);
  else
    set = DefaultInclude<S>(set);

  return set.Find(entityID);
}

protected virtual DbSet<S> DefaultInclude<S>(DbSet<S> set) where S : class
{
  // base implementation just returns the set
  // derived versions would attach Includes to the set before returning it
  return set;
}

protected virtual DbSet<S> FullInclude<S>(DbSet<S> set) where S : class
{
  // base implementation just returns the set
  // derived versions would attach Includes to the set before returning it
  return set;
}

Это мой базовый репозиторий, и я хотел бы иметь возможность переопределить эти XyzInclude методы в производном классе. Но мне нужно переопределить их не универсальной реализацией, и это, очевидно, не работает на уровне языка.

Это было действительно легко с Linq2Sql, поскольку я просто настраивал объект DataLoadOptions и присоединял его к контексту. С Include API на DbSet я не знаю, как лучше всего это сделать. Я ищу рекомендации по простой реализации шаблона Стратегии или аналогичного.

РЕДАКТИРОВАТЬ: Я думаю, суть моего вопроса на самом деле заключается в переопределении универсального метода с неуниверсальной производной версией. Я ищу шаблон или технику, которая позволяет мне это делать. Я смотрю на методы расширения, но предпочел бы более «чистое» решение, если у кого-то оно есть.

Ответы [ 2 ]

0 голосов
/ 09 июня 2011

Просто подумал, что я вернусь к этому с решением, которое я в конечном итоге.Мои репозитории не являются полностью общими, но все методы таковы, поэтому мне нужен был способ хранения включений для любого типа сущности и возможность извлекать их при вызове методов для этого типа.

Я позаимствовализ ответа Ладислава здесь , и я могу вернуться к этому плану, чтобы каждый отдельный метод, определенный в производных репозиториях, определял свои собственные включения, так как существует достаточно различных комбинаций того, что включать и что не включать, повторяянемного кода, определяющего то же самое включение в нескольких местах, вероятно, того стоит.Но в любом случае, это текущий дизайн, и он работает ...

Базовый репозиторий:

public abstract class Repository : IQueryableRepository, IWritableRepository
{
  private readonly DbContext _context;
  private readonly Dictionary<Type, LambdaExpression[]> _includes = new Dictionary<Type, LambdaExpression[]>();

  protected Repository(DbContextBase context)
  {
    _context = context;
    RegisterIncludes(_includes);
  }

  protected abstract void RegisterIncludes(Dictionary<Type, LambdaExpression[]> includes);

  protected S GetSingle<S>(Expression<Func<S, bool>> query, bool getChildren = false) where S : class
  {
    IQueryable<S> entities = _context.Set<S>().AsNoTracking();

    if (query != null)
      entities = entities.Where(query);

    entities = ApplyIncludesToQuery<S>(entities, getChildren);

    return entities.FirstOrDefault();
  }

  private IQueryable<S> ApplyIncludesToQuery<S>(IQueryable<S> entities, bool getChildren) where S : class
  {
    Expression<Func<S, object>>[] includes = null;

    if (getChildren && _includes.ContainsKey(typeof(S)))
      includes = (Expression<Func<S, object>>[])_includes[typeof(S)];

    if (includes != null)
      entities = includes.Aggregate(entities, (current, include) => current.Include(include));

    return entities;
  }
}

Производные репозитории должны только определять свои включения в одном месте, а затем при вызовеметод запроса, который вы просто указываете, хотите ли вы, чтобы дети включались или нет (см. getChildren выше).

public class DerivedRepository : Repository
{
  public DerivedRepository(DbContext context)
    : base(context) { }

  protected override void RegisterIncludes(Dictionary<Type, LambdaExpression[]> includes)
  {
    includes.Add(typeof(ParentType), new Expression<Func<ParentType, object>>[] { 
      p => p.SomeChildReference.SomeGrandchild,
      p => p.SomeOtherChildReference
    });
  }
}
0 голосов
/ 27 апреля 2011

Невозможно, если вы не сделаете класс полностью универсальным:

public class BaseRepo<S>
{
    protected virtual DbSet<S> DefaultInclude(DbSet<S> set) {return set;}
} 

public class ProductRepo : BaseRepo<Product>
{
    protected override DbSet<Product> DefaultInclude(DbSet<Product> set)
    {
       return set.Include("...");
    }
}
...