包含在(半)通用存储库中

发布于 2024-11-04 04:30:46 字数 1388 浏览 0 评论 0原文

我正在尝试弄清楚如何使用新的 EF Code First 内容,但在弄清楚如何将 Include 功能调整到半通用存储库类中时遇到了困难。 (我说半通用,因为类不是通用的,只是方法。所以我有一个存储库,本质上包装了一个聚合,并且您可以与属于该聚合一部分的所有实体交互,而不仅仅是单个实体。 )

我的情况是,我有一个实体,它有一个总是需要加载的子项,然后是仅可选加载的其他子项,我想在我的存储库方法中包含一个简单的 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,我对如何最好地做到这一点感到困惑。我正在寻找有关策略模式或类似模式的简单实现的建议。

编辑:我想我的问题的本质实际上是关于用非泛型派生版本覆盖泛型方法。我正在寻找一种模式或技术可以让我做到这一点。扩展方法是我正在关注的一件事,但如果有人有的话,我更喜欢更“纯粹”的解决方案。

I am trying to figure out how to use the new EF Code First stuff and I am having trouble figuring out how to adapt the Include functionality into a semi-generic Repository class. (I say semi-generic, because the class is not generic, just the methods. So I have one repository that wraps an aggregate, essentially, and you can interact with all Entities that are part of that aggregate, not just a single Entity.)

My situation is I have an Entity that has one child that always needs to be loaded, and then other children that are only optionally loaded, I want to include a simple bool parameter on my repository method. Like so:

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

That is my base Repository, and I'd like to be able to override those XyzInclude methods in a derived class. But I need to override them with a non-generic implementation, and this obviously doesn't work at a language level.

This was really easy with Linq2Sql in that I'd just configure a DataLoadOptions object and attach it to the context. With the Include API off of DbSet I'm stumped on how best to do this. I'm looking for recommendations on a simple implementation of a Strategy pattern or similar.

EDIT: I guess the essence of my question is really about overriding a generic method with a non-generic derived version. I'm looking for a pattern or technique that allows me to do that. Extension methods are one thing I'm looking at, but would prefer a more "pure" solution if anyone has one.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

蛮可爱 2024-11-11 04:30:46

除非您使该类完全通用,否则不可能:

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

Not possible unless you make the class fully generic,:

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("...");
    }
}
梨涡 2024-11-11 04:30:46

只是想我会用我最终得到的解决方案重新审视这个问题。我的存储库并不完全通用,但所有方法都是通用的,因此我需要一种方法来存储任何实体类型的包含内容,并能够在为该类型调用方法时将它们取出。

我借用了拉迪斯拉夫的答案这里,我可以重新审视这个设计,让派生存储库上定义的每个单独的方法都定义自己的包含,因为要包含的内容和不包含的内容有足够多的不同组合,因此在几个地方重复定义相同包含的一小部分代码可能是值得的。但无论如何,这是当前的设计并且它有效......

基础存储库:

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

Just thought I'd revisit this with the solution I ended up with. My repositories aren't fully generic, but all the methods are, so I needed a way to store includes for any entity type and be able to pull them out when the methods were called for that type.

I borrowed from Ladislav's answer here, and I may revisit this design to just have each individual method defined on the derived repositories define their own includes as there are enough different combinations of what to include and what not to include that repeating the little bit of code defining the same include in a few places is probably worth it. But anyway, this is the current design and it works...

Base repository:

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

Derived repositories only need to define their includes in the one place, and then when you call the query method you just specify if you want children included or not (see getChildren above).

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
    });
  }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文