【问题标题】:How to manage db context in n-tier asp.net application with dependecy injection?如何使用依赖注入管理 n 层 asp.net 应用程序中的数据库上下文?
【发布时间】:2017-04-14 03:26:42
【问题描述】:

我有 3 层架构 - asp.net web api、BLL 和 DAL。我使用 Ninject 作为依赖注入器,用于在层之间注入数据库上下文和对象。作为 ORM,我使用实体框架。 db 上下文的注入在 DAL 中处理。因此,每次实例化 BLL 中的某个存储库时,也会创建 db 上下文的新实例。我这样做是这样的:

public class UserRepository : IUserRepository
{
    private IChatDbModel _chatDbModel;

    public UserRepository(IChatDbModel chatDbModel)
    {
        this._chatDbModel = chatDbModel;
    }

有必要说,可以解决我的问题的 PerWebRequest 在比 web api 更低的层中不可用。只有 web api 层有关于 http 请求生命周期的信息,所以可以使用 Ninject.Web.Common 库。

我的问题是,有没有办法像在这个架构中使用 PerWebRequest 那样共享整个请求的数据库上下文?还是真的有必要为每个新的存储库实例创建新的数据库上下文实例?

编辑

我忘了提到在每一层中我都引用了 Ninject 库,并且我正在为特定层注册映射。 DAL 中的方法如下所示:

    public static void Register(IKernel kernel)
    {
        kernel.Bind<IChatDbModel>().To<ChatDbModel>();
    }

在 BLL 中是这样的:

    public static void Register(IKernel kernel)
    {
        kernel.Bind<IUserRepository>().To<UserRepository>();
        NinjectDataAccess.Register(kernel);
    }

在 API 中看起来像这样,它位于 NinjectWebCommon.cs:

    private static void RegisterServices(IKernel kernel)
    {
        kernel.Bind<IUserLogic>().To<UserLogic>();
        NinjectLogic.Register(kernel);            
    }     

所以在每一层中,我不仅要映射它自己的对象,还要调用位于下面的层的注册方法,如果有的话,使用这样的机制,我可以注册每一层的依赖映射而不引用所有层API,我不应该引用除 BLL 之外的任何其他层,所以在我的情况下是 DAL。如果我在 API 层中引用 DAL,则可以定义映射并调用 PerWebRequest,因为我会有对象,但我没有,我认为架构应该避免这种情况,还是我错了?

【问题讨论】:

  • 您的问题假设您的 BLL 和 DAL 对 Ninject 以及事物的注册方式一无所知;他们不应该。由于对象已在Composition Root 中注册(在您的情况下位于您的网络项目中),因此将您的DbContext 注册为PerWebRequest 应该没有问题。
  • 感谢您的回复,实际上我在每一层都定义了依赖映射,所以我在每一层都引用了 Ninject,以避免在 API 中引用 DAL——我认为应该避免建筑学。请检查我的编辑。
  • 您不应该在每一层中定义您的 DI 映射。请阅读this related q/a

标签: c# asp.net dependency-injection dbcontext data-access-layer


【解决方案1】:

您可以通过注册 OnePerRequestHttpModule http 模块来实现每个请求实例,该模块在内部使用 HttpContext 生命周期来跟踪注册的类型并在请求/响应生命周期结束时处理它们。

安装 Ninject.Web.Common 包后,在 NinjectWebCommon.cs 中你必须做的(安装 nuget 包后会自动添加)

DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule))

对于您的类型注册,您可以执行以下操作

//register IChatDbModel with per request scope
kernel.Bind<IChatDbModel>().To<ChatDbModel>().InRequestScope();

//register repositories with default transient scope
kernel.Bind<IUserRepository>().To<UserRepository>();

您的所有存储库都是瞬态的,因此无论在何处注入它们,都会提供一个单独的实例,但您的 DBContext 实例将根据请求创建和处置。

我假设您已将 BAL 和 DAL 引用添加到 web api 项目,以便 web api 项目可以访问 IChatDbModel 以在 Ninject 内核中执行类型注册。

【讨论】:

  • 感谢您的回复,但由于 API 中没有引用 DAL,这是不可能的。所以我没有像 ChatDbModel 这样的对象和它在 API 中的接口。请检查我的编辑。
  • 我能够在我最近的一个项目中使用 Unity DI 框架实现这一目标。每个应用层管理自己的 DI 注册,UI 层规定必须使用哪个生命周期管理器。 LifetimeManager 作为委托传递给其他层,无需在 BAL 或 DAL 中安装 Unity.MVC 或 Unity.AspNet.WebApi。如果您想查看示例,请告诉我。
  • 另外请记住,您尝试实现的干净分层方法可能不值得,因为 BAL 和 DAL 是您的 Web 层的运行时依赖项。仅仅因为您在 web 层中没有添加引用,仍然必须将 DAL 部署到 bin 文件夹。由于 Web 层是您的 DI 容器根,它应该了解所有依赖项(直接或间接)。我认为对于什么是正确的选择没有明确的共识。我尝试使用 UNITY DI 进行干净分离,并且效果很好,但我还没有决定什么是理想的方法。
  • 我想构建完全分离的 n 层应用程序,但经过大量阅读后,它似乎是不必要的,甚至更糟 - 适得其反。正如@Steven 所指出的,我最终将所有对象都引用到 Composition Root,这似乎是完美的解决方案。谢谢你们。
猜你喜欢
  • 2010-10-03
  • 2023-03-05
  • 2021-11-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-19
  • 2022-10-05
  • 1970-01-01
相关资源
最近更新 更多