我理解.NET Core中DI的基础知识,但我无法弄清楚如何在多个项目中使用它。想象一下,我在ASP.NET Core的Startup类中设置数据库上下文:
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控制器和数据访问类之间有许多层和功能时,人们会做什么?最终代码到达我的存储库类,这是实际需要上下文的类。它看起来像这样:
public class AlbumRepository
{
private GalleryDb _ctx;
public AlbumRepository(GalleryDb ctx)
{
_ctx = ctx;
}
public void Save(AlbumEntity entity)
{
// Use _ctx to persist to DB.
}
}
我理解我可以从API入口点一直传递上下文,但这似乎是一个反模式,因为它意味着通过多个对它感兴趣的类和函数将它作为参数传递。 / p>
相反,我想在我调用存储库类时执行类似的操作:
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类,如ASP.NET Identity的RoleManager和SignInManager。
答案 0 :(得分:4)
我理解我可以从API入口点一直传递上下文,但这似乎是一个反模式,因为它意味着通过多个对它感兴趣的类和函数将它作为参数传递。 / p>
不,那不是反模式。这就是 应该这样做的方式。然而,关于&#34;对它没有兴趣的类和函数&#34;没有意义。
简单地说,如果您正在使用包含DbContext
的存储库(顺便说一句可怕的想法,但我们会在其中添加一个引脚),那么您应该&直接与DbContext
打交道。相反,您应该将存储库注入控制器,然后简单地将上下文注入到:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<GalleryDb>();
services.AddScoped<AlbumRepository>();
}
由于ASP.NET Core知道如何注入GalleryDb
,而AlbumRepository
将GalleryDb
作为构造函数参数,因此您只需注册AlbumRepository
进行注入(使用&#34;范围&#34;或请求生命周期。)
现在,您可以按照当前注入上下文的方式注入AlbumRepository
:
public class AlbumController : Microsoft.AspNetCore.Mvc.Controller
{
private AlbumRepository _repo;
public AlbumController(AlbumRepository repo)
{
_repo = repo;
}
}
当这个开始变得棘手时,就是当你有许多存储库时,特别是如果你有需要与多个存储库交互的控制器。最终,您的代码将成为服务配置和注入样板的老鼠窝。但是,在这一点上,您应该真正使用工作单元模式,将所有存储库封装在一个可以注入的类中。但是等等,哦,是的,DbContext
已经已经。它是封装多个存储库或DbSet
的工作单元。这就是您不应该将存储库模式与Entity Framework结合使用的原因。它是一个毫无意义的抽象,除了为你的代码添加额外的和不必要的熵之外什么都不做。
如果你想抽象DbContext
,那么你应该使用类似服务层模式的东西(不要与微软称为&#34;服务模式&#34;的RPC牛排泄物混淆)或CQRS(命令查询责任分离)模式。存储库模式是一回事:抽象掉原始SQL。如果您没有原始SQL,则不应该实现该模式。
答案 1 :(得分:2)
Chris-pratt的关键突破让我明白,唯一可行的方法是通过所有层来使用DI。例如,在数据层中,我通过DI获得DB上下文:
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来获取对业务层类的引用:
[Route("api/[controller]")]
public class AlbumController : Microsoft.AspNetCore.Mvc.Controller
{
private Album _album;
public AlbumController (Album album)
{
_album = album;
}
}
通过在每一层使用DI,DI系统能够在需要的地方构建所有必要的类。
此要求对应用程序的体系结构产生了深远的影响,我现在意识到,我最初希望调整现有的非DI应用程序以开始在数据库环境中使用DI,这是一项重大任务。