Dependency Injection in abstract class, ASP.NET Core
在我的 ASP.NET Core 2 项目中,我创建了继承 RazorPage
类的新类,以在视图中添加一些额外的属性。
public abstract class WebViewPage<TModel> : Microsoft.AspNetCore.Mvc.Razor.RazorPage<TModel>
{
public string Version
{
get
{
return "1.00.100";
}
}
public User CurrentUser
{
get
{
//challenge is here:
//return _services.GetRequiredService<IUserService>().GetCurrentUser();
}
}
}
public abstract class WebViewPage : WebViewPage<dynamic>
{
}
和 _ViewImport.cshtml
文件更改为:
@inherits MyProject.WebViewPage<TModel>
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
该项目工作正常,直到挑战代码被标记为评论。 但是,我怎样才能将依赖项注入抽象类呢? 或者如何解决抽象类中的服务?
这是我的 StartUp.cs
文件:
public void ConfigureServices(IServiceCollection services)
{
services.AddMemoryCache();
services.AddMvc();
services.AddTransient<IUserService, UserService>();
//services.AddScoped<IRazorPage<?>, WebViewPage<?>(); // ?: challenge!
}
答案
如果您需要访问razor 页面内的 service provider,您可以通过 Context
属性访问 HttpContext
,例如:
var userService = Context.RequestServices.GetRequiredService<IUserService>();
一个可能更好的选择是拥有一个将 IUserService
作为依赖项的构造函数。 因此,您的类型的实现必须传递该依赖性,本质上要求它们具有自己的构造函数依赖性(这是一个很好的显式设计)。
至于那个依赖注册部分,注意你一般不能注册抽象类,因为它们不能被实例化。
但一般来说,为了注册泛型类型,您必须使用更详细的 AddScoped
重载,它采用实际Type
参数:
services.AddScoped(typeof(IRazorPage<>), typeof(WebViewPage<>));
进行泛型方法调用需要一个真实类型作为泛型类型参数,因此您必须在 IRazorPage<T>
和 WebViewPage<T>
中为类型参数 T
指定一个具体类型。 由于您明确不想在那里使用具体类型,而是想告诉依赖注入容器支持任何类型,因此您不能在那里使用此语法。
由于 typeof
运算符可以确定实际的泛型类型,因此您可以使用它而不必使用将注册限制为单一类型的特定类型参数。
How to resolve parent class instance from registered child class in Asp.net core dependency injection
假设我有一个类
abstract class CustomContext: IdentityDbContext<AppUser, Role>{}
class TContext: CustomContext{}
在服务注册中
services.AddDbContext<TContext>();
在上面,我可以像下面这样解析 TContext
的实例
var context = ServicesCollector.Container.GetService(typeof(TContext));
但是在程序的某处,我想解析 CustomContext
的实例。
答案
向解析实现的委托工厂注册抽象。
services.AddScoped<CusomContext>(sp => sp.GetRequiredService<TContext>());
这样,当请求抽象时,尝试解析它的容器就会知道它。
ASP.Net Core MVC: Dependency Injection by Base Type
在转向 ASP.Net Core 之前,我使用了 Ninject
,它允许我以通用方式注入我的实体框架数据库上下文。 例如:
Bind<DbContext>().To<MyActualDbContext>();
在 ASP.Net Core MVC 中,当使用 AddDbContext
时,如果我执行以下操作,将导致服务无法解析。
AddDbContext<MyActualDbContext>(x=> x.UseSqlServer("..."));
---
public class TestController: Controller {
public DbContext Db {get; private set;}
public TestController(DbContext Db){
this.Db = Db;
}
}
我知道我可以通过使用 AddSingleton
或 AddScoped
来解决这个问题,但是,由于 AddDbContext
是专门开发用于处理 EF 数据上下文的,我想知道是否有任何方法可以使用该方法实现此目的。
如果 AddDbContext
不支持此行为,那么 AddSingleton
或 AddScoped
哪个替代方法更好? 我知道 AddScoped
每次请求都会实例化服务一次,但我想知道 Singleton
是否会导致任何潜在问题。 我想确保在 ED DbContext
的情况下使用这两种方法不会有任何可能的缺点。
答案
您可以使用
// Or use AddTransient, makes no difference as it only wraps around the
// container resolve method
services.AddScoped<DbContext>(provider => provider.GetRequiredService<MyActualDbContext>());
但老实说,我看不出这样做有什么好处,因为基类不会有任何 DbSet
,所以如果不将它转换为原始代码就不能使用它,但这在某种程度上打败了这个想法 完全有IoC。
是的,单例 DbContext
是有问题的。 它可能会导致内存泄漏(跟踪缓存会不断增长),如果您将其放置在某个地方,DI
系统将始终返回已放置的单例上下文实例。