具有通用存储库、依赖项注入和 SoC 的 EF6 Code First

2024-05-06

经过大量阅读和尝试之后Entity Framework最新稳定版本(6.1.1)。

我读到了很多关于是否使用存储库的矛盾EF6 or EF一般来说,因为它是DbContext已经提供了一个存储库并且DbSet the UoW, 盒子外面。

让我首先解释一下我的解决方案在项目方面包含哪些内容,然后我会回到矛盾之处。

它有一个类库项目和一个asp.net-mvc项目。类库项目是数据访问以及在哪里Migrations已启用Code First.

在我的类库项目中,我有一个通用存储库:

public interface IRepository<TEntity> where TEntity : class
{
    IEnumerable<TEntity> Get();

    TEntity GetByID(object id);

    void Insert(TEntity entity);

    void Delete(object id);

    void Update(TEntity entityToUpdate);
}

这是它的实现:

public class Repository<TEntity> where TEntity : class
{
    internal ApplicationDbContext context;
    internal DbSet<TEntity> dbSet;

    public Repository(ApplicationDbContext context)
    {
        this.context = context;
        this.dbSet = context.Set<TEntity>();
    }

    public virtual IEnumerable<TEntity> Get()
    {
        IQueryable<TEntity> query = dbSet;
        return query.ToList();
    }

    public virtual TEntity GetByID(object id)
    {
        return dbSet.Find(id);
    }

    public virtual void Insert(TEntity entity)
    {
        dbSet.Add(entity);
    }

    public virtual void Delete(object id)
    {
        TEntity entityToDelete = dbSet.Find(id);
        Delete(entityToDelete);
    }

    public virtual void Update(TEntity entityToUpdate)
    {
        dbSet.Attach(entityToUpdate);
        context.Entry(entityToUpdate).State = EntityState.Modified;
    }
}

这里有一些实体:

public DbSet<User> User{ get; set; }
public DbSet<Order> Orders { get; set; }
public DbSet<UserOrder> UserOrders { get; set; }
public DbSet<Shipment> Shipments { get; set; }

我不知道要重复什么但是EF6您不再传递存储库,但是DbContext反而。因此对于DI我在中设置了以下内容asp-net-mvc项目使用Ninject:

private static void RegisterServices(IKernel kernel)
{
    kernel.Bind<ApplicationDbContext>().ToSelf().InRequestScope();
}

这将注入ApplicationDbContext在适用的情况下,通过构造函数注入到上层类。

现在回到矛盾。

如果我们不再需要存储库,因为EF已经提供了开箱即用的功能,我们该怎么做Separation of Concern(标题中缩写为SoC)?

如果我错了,请纠正我,但在我看来,我只需要执行所有数据访问逻辑/计算(例如添加、获取、更新、删除以及此处和那里的一些自定义逻辑/计算(特定于实体))在里面asp.net-mvc项目,如果我不添加存储库。

非常感谢对此事的任何了解。


一些解释有望消除您的困惑。存储库模式的存在是为了抽象数据库连接和查询逻辑。 ORM(对象关系映射器,如 EF)已经以一种或另一种形式存在了很长时间,以至于许多人已经忘记或从未享受过处理散布着 SQL 查询和语句的意大利面条代码的巨大乐趣和乐趣。时间是,如果你想查询数据库,你实际上要负责一些疯狂的事情,比如启动连接和实际从以太构建 SQL 语句。存储库模式的目的是为您提供一个地方来放置所有这些肮脏的东西,远离美丽的原始应用程序代码。

快进到 2014 年,实体框架和其他 ORMare你的存储库。所有 SQL 逻辑都整齐地打包在一起,远离您的窥探,而是您可以在代码中使用一个很好的编程 API。从一方面来说,这已经足够抽象了。它唯一没有涵盖的是对 ORM 本身的依赖。如果您后来决定将实体框架换成 NHibernate 甚至 Web API 之类的东西,那么您必须对您的应用程序进行手术才能做到这一点。因此,添加另一层抽象仍然是一个好主意,但不是存储库,或者至少可以说是典型的存储库。

您拥有的存储库是typical存储库。它只是为实体框架 API 方法创建代理。你打电话repo.Add和存储库称为context.Add。坦率地说,这很荒谬,这就是为什么许多人,包括我自己,说不要将存储库与实体框架一起使用.

So what should你做?创建服务,或者最好的说法是“类似服务的类”。当开始讨论与 .NET 相关的服务时,突然间您会谈论与我们在此讨论的内容完全无关的各种事物。类似服务的类就像服务一样,它具有返回特定数据集或对某些数据集执行非常特定的功能的端点。例如,使用典型的存储库时,您会发现自己在执行以下操作:

articleRepo.Get().Where(m => m.Status == PublishStatus.Published && m.PublishDate <= DateTime.Now).OrderByDescending(o => o.PublishDate)

您的服务类将像这样:

service.GetPublishedArticles();

看,所有符合“已发表文章”资格的逻辑都整齐地包含在端点方法中。此外,使用存储库,您仍然会暴露底层 API。它是easier切换到其他东西,因为基本数据存储是抽象的,但如果用于查询该数据存储的 API 发生变化,您仍然会陷入困境。

UPDATE

设置非常相似;区别主要在于如何使用服务与存储库。也就是说,我什至不会让它依赖于实体。换句话说,您本质上是为每个上下文而不是每个实体提供一个服务。

一如既往,从界面开始:

public interface IService
{
    IEnumerable<Article> GetPublishedArticles();

    ...
}

然后,你的实现:

public class EntityFrameworkService<TContext> : IService
    where TContext : DbContext
{
    protected readonly TContext context;

    public EntityFrameworkService(TContext context)
    {
        this.context = context;
    }

    public IEnumerable<Article> GetPublishedArticles()
    {
        ...
    }
}

然后,事情开始变得有点棘手。在示例方法中,您可以简单地引用DbSet直接,即context.Articles,但这意味着关于DbSet上下文中的名称。最好用context.Set<TEntity>(),以获得更大的灵活性。在我跳太多火车之前,我想指出为什么我命名这个EntityFrameworkService。在您的代码中,您只会引用您的IService界面。然后,通过依赖注入容器,您可以替换EntityFrameworkService<YourContext>为了那个原因。这开启了创建其他服务提供商的能力,例如WebApiService, etc.

现在,我喜欢使用单个受保护的方法,该方法返回我的所有服务方法都可以利用的可查询对象。这消除了很多麻烦,比如必须初始化一个DbSet每次实例通过var dbSet = context.Set<YourEntity>();。这看起来有点像:

protected virtual IQueryable<TEntity> GetQueryable<TEntity>(
    Expression<Func<TEntity, bool>> filter = null,
    Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
    string includeProperties = null,
    int? skip = null,
    int? take = null)
    where TEntity : class
{
    includeProperties = includeProperties ?? string.Empty;
    IQueryable<TEntity> query = context.Set<TEntity>();

    if (filter != null)
    {
        query = query.Where(filter);
    }

    foreach (var includeProperty in includeProperties.Split
        (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
    {
        query = query.Include(includeProperty);
    }

    if (orderBy != null)
    {
        query = orderBy(query);
    }

    if (skip.HasValue)
    {
        query = query.Skip(skip.Value);
    }

    if (take.HasValue)
    {
        query = query.Take(take.Value);
    }

    return query;
}

请注意,该方法首先是受保护的。子类可以利用它,但这应该确实不属于公共 API 的一部分。这个练习的重点是不暴露可查询对象。其次,它是通用的。换句话说,只要上下文中存在某些内容,它就可以处理您向其抛出的任何类型。

然后,在我们的小示例方法中,您最终会执行以下操作:

public IEnumerable<Article> GetPublishedArticles()
{
    return GetQueryable<Article>(
        m => m.Status == PublishStatus.Published && m.PublishDate <= DateTime.Now,
        m => m.OrderByDescending(o => o.PublishDate)
    ).ToList();
}

这种方法的另一个巧妙技巧是能够利用接口提供通用服务方法。假设我希望能够有一种方法来发布anything。我可以有一个像这样的界面:

public interface IPublishable
{
    PublishStatus Status { get; set; }
    DateTime PublishDate { get; set; }
}

然后,任何可发布的实体都将实现该接口。完成后,您现在可以执行以下操作:

public IEnumerable<TEntity> GetPublished<TEntity>()
    where TEntity : IPublishable
{
    return GetQueryable<TEntity>(
        m => m.Status == PublishStatus.Published && m.PublishDate <= DateTime.Now,
        m => m.OrderByDescending(o => o.PublishDate)
    ).ToList();
}

然后在您的应用程序代码中:

service.GetPublished<Article>();
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

具有通用存储库、依赖项注入和 SoC 的 EF6 Code First 的相关文章

随机推荐

  • 软删除 Nibernate

    我想对我的数据库表进行软删除 我应用了以下语句 如此处所述http nhibernate info blog 2008 09 06 soft deletes html http nhibernate info blog 2008 09 06
  • 如何在Java中设置word文档(.doc或.docx)的背景颜色(页面颜色)?

    通过一些图书馆 例如http poi apache org http poi apache org 我们可以创建Word文档具有任何文本颜色 但对于背景或文本的突出显示 我没有找到任何解决方案 手动方式的word页面颜色 https sup
  • 如何将非托管内存数组复制到同一个非托管内存中

    我保留了内存10项128字节 IntPtr dst Marshal AllocHGlobal 10 128 IntPtr src1 Marshal AllocHGlobal 128 init scr1 from DLL IntPtr src
  • SuiteScript 2.0 添加过滤器以保存脚本中的搜索

    我有一个自定义记录 其中有一个项目字段和一个位置字段 我对该记录保存了一个搜索 其中已经包含我想要的列和一些始终需要的起始条件 我想在处理销售订单时使用此搜索 我想存储行上所有项目内部 id 和位置 id 的数组 然后将其作为动态过滤器传递
  • 获取线性 pyomo 约束的系数

    我想获得线性约束的系数cpyomo 模型的m 例如 对于 m ConcreteModel m x 1 Var m x 2 Var m x 3 Var within Integers m x 4 Var within Integers m c
  • 在 AtTask PUT 请求中发送多个更新

    我想知道 AtTask 的 API 中是否有一种方法可以在单个 URL 请求中发布多个更新 例如 我需要更新 1 000 条记录的 extRefID 我是否可以对 API 进行 1 000 次调用 就开销而言成本高昂 或者我可以使用包含如下
  • C++ 构造函数抛出异常时销毁对象的成员变量

    这个问题是基于 Scott Meyers 在他的书 更有效的 C 中提供的一个例子 考虑下面的类 A class to represent the profile of a user in a dating site for animal
  • 我可以在 pandas 中执行动态行累加吗?

    如果我有以下数据框 如下导出 df pd DataFrame np random randint 0 10 size 10 1 0 0 0 1 2 2 8 3 1 4 0 5 0 6 7 7 0 8 2 9 2 有没有有效的方法cumsum
  • 使用 ImageMagick 进行 SVG 转换无法正确应用翻译

    我使用的是 Mac OS X 10 5 的 Mac 我正在尝试使用 ImageMagick 来转换SVG http en wikipedia org wiki Scalable Vector Graphics文件到一个PNG http en
  • 在二维平面中找到距离 P 点最近的 K 个点

    资料来源 亚马逊面试问题 解决方案1制作大小为 K 的堆并按最小距离收集点O NLogK 复杂 解决方案2 取大小为 N 的数组并按距离排序 应该使用QuickSort 霍尔修改 取前 K 点作为答案 这太复杂了 NlogN 但可以优化到近
  • 实现特征时“预期类型参数,找到结构”

    我正在尝试为有向图结构创建一个特征并提供一个非常基本的实现 但遇到编译器错误 pub trait DiGraph lt a gt type N fn nodes
  • 使用 vs code,如何让 scala 格式工作并格式化我的代码?

    我的多项目 sbt 存储库中有 scala 格式插件 addSbtPlugin org scalameta sbt scalafmt 2 3 2 所以在 sbt 控制台中如果我运行 scalafmt 它工作正常 我的 build sbt 有
  • 如何使用java与防火墙(路由器)建立ssh连接?

    由于某种原因 我需要连接到防火墙 基于Linux 并使用Java添加一些规则 用google搜索了一段时间后 我发现jsch是我最好的选择 但是当我 用它来执行命令 显示主机名 例如 返回错误 如果我 执行类似命令 ls l and who
  • 删除 ChildEventListener

    我正在使用 FirebasechildEventListener被添加 N 次到Node从数据库获取数据 现在我正在尝试删除childEventListener它似乎不起作用 我得到了重复的数据 query FirebaseDatabase
  • 如何将堆栈跟踪发送到 log4j?

    假设您捕获了一个异常 并在标准输出 例如控制台 上得到以下内容 如果您执行e printStackTrace java io FileNotFoundException so txt at java io FileInputStream
  • 如何修复实例上的错误:未定义的变量 B?

    我想编译此 Verilog 代码 但在实例中出现错误B模块中的MultiP module error 1 Undefined variable B error 2 near Adder1 syntax error unexpected ID
  • NSIS - 在命令行安装期间打印提示

    我正在使用 NSIS 为 Windows 制作安装程序 并且有许多用户可以使用命令行指定的自定义安装选项 例如 installer exe IDPATH c Program Files Adobe Adobe InDesign CS5 S
  • 在Application中显示Android application.apk的创建日期

    我正在开发一个android应用程序 我想显示版本 apk和日期现在我可以使用显示应用程序的应用程序版本PackageInfo现在我想显示应用程序创建的日期或 apk创建日期 对于新读者 public static String getAp
  • WPF 图片库

    我将驱动一个需要向用户呈现图像组的触摸屏应用程序 不是网络应用程序 我们的愿望是呈现具有页面前进 后退功能的 3x3 图像网格 他们可以选择一些 我将只展示这些图像 我没看到ListView完全符合我的要求 尽管 WPF 足够大 我很可能错
  • 具有通用存储库、依赖项注入和 SoC 的 EF6 Code First

    经过大量阅读和尝试之后Entity Framework最新稳定版本 6 1 1 我读到了很多关于是否使用存储库的矛盾EF6 or EF一般来说 因为它是DbContext已经提供了一个存储库并且DbSet the UoW 盒子外面 让我首先