如何使用.NET Core解析类库中的DI类?

时间:2017-10-06 21:55:12

标签: dependency-injection asp.net-core .net-core asp.net-core-2.0

我理解.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。

2 个答案:

答案 0 :(得分:4)

  

我理解我可以从API入口点一直传递上下文,但这似乎是一个反模式,因为它意味着通过多个对它感兴趣的类和函数将它作为参数传递。 / p>

不,那不是反模式。这就是 应该这样做的方式。然而,关于&#34;对它没有兴趣的类和函数&#34;没有意义。

简单地说,如果您正在使用包含DbContext的存储库(顺便说一句可怕的想法,但我们会在其中添加一个引脚),那么您应该&直接与DbContext打交道。相反,您应该将存储库注入控制器,然后简单地将上下文注入到:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<GalleryDb>();
    services.AddScoped<AlbumRepository>();
}

由于ASP.NET Core知道如何注入GalleryDb,而AlbumRepositoryGalleryDb作为构造函数参数,因此您只需注册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,这是一项重大任务。