如何使用 .NET Core 解析 class 库中的 DI class?
How to resolve a DI class in a class library with .NET Core?
我了解 .NET Core 中 DI 的基础知识,但我无法弄清楚如何将它用于多个项目。想象一下,我正在 ASP.NET Core:
的 Startup class 中设置数据库上下文
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<GalleryDb>();
}
我知道如何在 API 控制器中访问该上下文:
public class AlbumController : Microsoft.AspNetCore.Mvc.Controller
{
private GalleryDb _ctx;
public AlbumController(GalleryDb ctx)
{
_ctx = ctx;
}
}
但是当 API 控制器和数据访问 class 之间有许多层和功能时,该怎么办?最终代码到达我的存储库 class,这是实际需要上下文的存储库。它看起来像这样:
public class AlbumRepository
{
private GalleryDb _ctx;
public AlbumRepository(GalleryDb ctx)
{
_ctx = ctx;
}
public void Save(AlbumEntity entity)
{
// Use _ctx to persist to DB.
}
}
我知道我可以从 API 入口点一直向下传递上下文,但这似乎是一种反模式,因为这意味着将它作为参数传递给多个 class对它不感兴趣的es和函数。
相反,我想在调用存储库时做这样的事情 class:
public void Save(AlbumEntity album)
{
var ctx = DependencyResolver.GetInstance<GalleryDb>();
var repo = new AlbumRepository(ctx);
repo.Save(album);
}
我相信一些 DI 框架有类似的东西,但我正在尝试弄清楚如何使用原生 .NET Core 2.0 来做到这一点。这可能吗?最佳做法是什么?我发现一个线程 (ASP.NET Core DependencyResolver) 谈论使用 IServiceProvider 但暗示这不是一个理想的解决方案。
我希望无论解决方案是什么,我都可以将其扩展以应用于其他 DI classes,例如 ASP.NET Identity 的 RoleManager 和 SignInManager。
I understand that I could pass the context from the API entry point all the way down, but that seems like an anti-pattern because it means passing it as a parameter through multiple classes and functions that have no interest in it.
不,那不是反模式。这就是您应该的方式。但是,关于 "classes and functions that have no interest in it" 的部分毫无意义。
简单地说,如果您正在使用类似于包装 DbContext
的存储库之类的东西(顺便说一下,这是一个可怕的想法,但我们会在其中插入一个图钉),那么您不应该永远直接处理那个 DbContext
。相反,您应该将您的存储库注入到您的控制器中,然后简单地将上下文注入到其中:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<GalleryDb>();
services.AddScoped<AlbumRepository>();
}
因为 ASP.NET Core 知道如何注入 GalleryDb
,并且 AlbumRepository
将 GalleryDb
作为构造函数参数,您只需将 AlbumRepository
注册为注入很好(使用 "scoped" 或请求生命周期)。
现在,您可以像当前注入上下文一样注入 AlbumRepository
:
public class AlbumController : Microsoft.AspNetCore.Mvc.Controller
{
private AlbumRepository _repo;
public AlbumController(AlbumRepository repo)
{
_repo = repo;
}
}
当您有很多存储库时,这开始变得棘手,尤其是当您有需要与多个存储库交互的控制器时。最终,您的代码将成为服务配置和注入样板的老鼠窝。但是,到那时,您实际上也应该使用工作单元模式,将所有存储库封装在一个 class 中,您可以改为注入。但是等等,哦,是的,这就是 DbContext
已经 。它是封装多个存储库或 DbSet
s 的工作单元。这就是为什么您不应该将存储库模式与 Entity Framework 结合使用。这是一个毫无意义的抽象,除了给你的代码添加额外的和不必要的熵之外什么都不做。
如果你想抽象DbContext
,那么你应该使用服务层模式(不要与微软称为"service pattern"的RPC牛粪混淆)或CQRS (命令查询责任分离)模式。存储库模式是为了一件事:抽象出原始 SQL。如果您没有原始 SQL,则不应实施该模式。
chris-pratt 帮助我理解的关键突破是,唯一可行的方法是通过 所有 层使用 DI。例如,在数据层下方,我通过 DI 获得了一个数据库上下文:
public class AlbumRepository
{
private GalleryDb _ctx;
public AlbumRepository(GalleryDb ctx)
{
_ctx = ctx;
}
}
在业务层我使用DI来获取对数据层的引用:
public class Album
{
private AlbumRepository _repo;
public Album(AlbumRepository repo)
{
_repo = repo;
}
}
然后,在web层,我使用DI获取业务层的引用class:
[Route("api/[controller]")]
public class AlbumController : Microsoft.AspNetCore.Mvc.Controller
{
private Album _album;
public AlbumController (Album album)
{
_album = album;
}
}
通过在每一层使用 DI,DI 系统能够在需要的地方构建所有必要的 classes。
此要求对应用程序的体系结构产生了深远的影响,我现在意识到,我最初希望调整现有的非 DI 应用程序以开始将 DI 用于 DB 上下文是一项艰巨的任务。
我了解 .NET Core 中 DI 的基础知识,但我无法弄清楚如何将它用于多个项目。想象一下,我正在 ASP.NET Core:
的 Startup class 中设置数据库上下文public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<GalleryDb>();
}
我知道如何在 API 控制器中访问该上下文:
public class AlbumController : Microsoft.AspNetCore.Mvc.Controller
{
private GalleryDb _ctx;
public AlbumController(GalleryDb ctx)
{
_ctx = ctx;
}
}
但是当 API 控制器和数据访问 class 之间有许多层和功能时,该怎么办?最终代码到达我的存储库 class,这是实际需要上下文的存储库。它看起来像这样:
public class AlbumRepository
{
private GalleryDb _ctx;
public AlbumRepository(GalleryDb ctx)
{
_ctx = ctx;
}
public void Save(AlbumEntity entity)
{
// Use _ctx to persist to DB.
}
}
我知道我可以从 API 入口点一直向下传递上下文,但这似乎是一种反模式,因为这意味着将它作为参数传递给多个 class对它不感兴趣的es和函数。
相反,我想在调用存储库时做这样的事情 class:
public void Save(AlbumEntity album)
{
var ctx = DependencyResolver.GetInstance<GalleryDb>();
var repo = new AlbumRepository(ctx);
repo.Save(album);
}
我相信一些 DI 框架有类似的东西,但我正在尝试弄清楚如何使用原生 .NET Core 2.0 来做到这一点。这可能吗?最佳做法是什么?我发现一个线程 (ASP.NET Core DependencyResolver) 谈论使用 IServiceProvider 但暗示这不是一个理想的解决方案。
我希望无论解决方案是什么,我都可以将其扩展以应用于其他 DI classes,例如 ASP.NET Identity 的 RoleManager 和 SignInManager。
I understand that I could pass the context from the API entry point all the way down, but that seems like an anti-pattern because it means passing it as a parameter through multiple classes and functions that have no interest in it.
不,那不是反模式。这就是您应该的方式。但是,关于 "classes and functions that have no interest in it" 的部分毫无意义。
简单地说,如果您正在使用类似于包装 DbContext
的存储库之类的东西(顺便说一下,这是一个可怕的想法,但我们会在其中插入一个图钉),那么您不应该永远直接处理那个 DbContext
。相反,您应该将您的存储库注入到您的控制器中,然后简单地将上下文注入到其中:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<GalleryDb>();
services.AddScoped<AlbumRepository>();
}
因为 ASP.NET Core 知道如何注入 GalleryDb
,并且 AlbumRepository
将 GalleryDb
作为构造函数参数,您只需将 AlbumRepository
注册为注入很好(使用 "scoped" 或请求生命周期)。
现在,您可以像当前注入上下文一样注入 AlbumRepository
:
public class AlbumController : Microsoft.AspNetCore.Mvc.Controller
{
private AlbumRepository _repo;
public AlbumController(AlbumRepository repo)
{
_repo = repo;
}
}
当您有很多存储库时,这开始变得棘手,尤其是当您有需要与多个存储库交互的控制器时。最终,您的代码将成为服务配置和注入样板的老鼠窝。但是,到那时,您实际上也应该使用工作单元模式,将所有存储库封装在一个 class 中,您可以改为注入。但是等等,哦,是的,这就是 DbContext
已经 。它是封装多个存储库或 DbSet
s 的工作单元。这就是为什么您不应该将存储库模式与 Entity Framework 结合使用。这是一个毫无意义的抽象,除了给你的代码添加额外的和不必要的熵之外什么都不做。
如果你想抽象DbContext
,那么你应该使用服务层模式(不要与微软称为"service pattern"的RPC牛粪混淆)或CQRS (命令查询责任分离)模式。存储库模式是为了一件事:抽象出原始 SQL。如果您没有原始 SQL,则不应实施该模式。
chris-pratt 帮助我理解的关键突破是,唯一可行的方法是通过 所有 层使用 DI。例如,在数据层下方,我通过 DI 获得了一个数据库上下文:
public class AlbumRepository
{
private GalleryDb _ctx;
public AlbumRepository(GalleryDb ctx)
{
_ctx = ctx;
}
}
在业务层我使用DI来获取对数据层的引用:
public class Album
{
private AlbumRepository _repo;
public Album(AlbumRepository repo)
{
_repo = repo;
}
}
然后,在web层,我使用DI获取业务层的引用class:
[Route("api/[controller]")]
public class AlbumController : Microsoft.AspNetCore.Mvc.Controller
{
private Album _album;
public AlbumController (Album album)
{
_album = album;
}
}
通过在每一层使用 DI,DI 系统能够在需要的地方构建所有必要的 classes。
此要求对应用程序的体系结构产生了深远的影响,我现在意识到,我最初希望调整现有的非 DI 应用程序以开始将 DI 用于 DB 上下文是一项艰巨的任务。