ASP.NET Core的底层设计支持和使用依赖注入。ASP.NET Core 应用程序能够利用内置的框架服务将服务注入到启动类的方法中,而且应用程序服务也能够配置注入。由ASP.NET Core 提供的默认服务容器提供了最小功能集,并非取代其余容器。html
1.浅谈依赖注入设计模式
依赖注入(Dependency injection,DI)是一种实现对象和依赖者之间松耦合的技术,将类用来执行其操做的这些对象以注入的方式提供给该类,而不是直接实例化依赖项或者使用静态引用。通常状况,类会经过构造函数声明器2依赖关系,容许他们遵循显示依赖原则。这种方法称为“构造函数注入”。api
当类的设计使用DI思想时,他们的耦合更加松散,由于他们没有对他们的合做者直接硬编码的依赖。这遵循“依赖倒置原则”,其中指出,高层模块不该该依赖于底层模块:二者都依赖于抽象。cookie
类要求在他们构造时向其提供抽象(一般是接口),而不是引用特定的实现。提取接口的依赖关系和提供接口的实现做为参数也是“策略设计模式”的一个示例。框架
当一个类被用来建立类及其相关的依赖关系时,这个成为容器(containers),或者称为控制反转(Inversion of Control, IoC)容器,或者依赖注入容器。容器本质上是一个工厂,负责提供向它请求的类型的实例。若是一个给定类型声明它具备依赖关系,而且容器已经被配置为其提供依赖关系,那么它将把建立依赖关系做为建立请求实例的一部分。除了建立对象的依赖关系外,容器一般还会管理应用程序中对象的生命周期。async
ASP.NET Core 包含一个默认支持构造函数注入的简单内置容器,ASP.NET 的容器指的是它管理的类型services,能够在Startup类的ConfigureServices方法中配置内置容器的服务。ide
2. 使用ASP.NET Core提供的服务函数
Startup类的ConfigureServices方法负责定义应用程序将使用的服务,包括平台自带的功能,好比,Entity Framework Core 和 ASP.NET Core MVC。除了IServiceCollection提供的几个服务以外,可使用一些扩展方法(AddDbContext,AddMvc,AddTransient等)向容器添加和注册额外服务: 测试
public void ConfigureServices(IServiceCollection services) { services.Configure<CookiePolicyOptions>(options => { // This lambda determines whether user consent for non-essential cookies is needed for a given request. options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.None; }); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); services.AddDbContext<AccessManagementContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"), providerOptions => providerOptions.EnableRetryOnFailure())); services.AddTransient<ICompanyServices, CompanyServices>(); }
ASP.NET Core 提供的功能和中间件,遵循约定使用一个单一的AddService扩展方法来注册全部该功能所需的服务。ui
3.注册本身的服务
咱们能够按照 services.AddTransient<ICompanyServices, CompanyServices>(); 这种写法注册本身的服务。第一个范型类型表示将要从容器中请求的类型(一般是一个接口)。第二个范型类型表示将由容器实例化而且用于完成请求的具体类型。
AddTransient 方法用于将抽象类型映射到为每个须要它的对象分别实例化的具体服务。为注册的每个服务选择合适的生命周期很重要,后面会介绍到。
下面是示例是注册本身的服务:
1.接口
public interface IAccountServices { Task<List<AccountViewModel>> GetList(); }
2.实现类
public class AccountServices:IAccountServices { AccessManagementContext _context; public AccountServices(AccessManagementContext context) { _context = context;//在构造函数中注入 } public async Task<List<Account>> GetList() { try { var query = _context.Account.ToListAsync(); return query ; } catch (Exception ex) { return null; } } }
3.在ConfigureServices中注册自定义的服务和EF上下文AccessManagementContext
public void ConfigureServices(IServiceCollection services) { services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); services.AddDbContext<AccessManagementContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"), providerOptions => providerOptions.EnableRetryOnFailure())); services.AddTransient<IAccountServices,AccountServices>(); }
4.在Controller构造函数中依赖注入
public class AccountController : Controller { private IAccountServices _accountServices; public AccountController(IAccountServices accountServices) { _accountServices = accountServices; } // GET: Account public async Task<ActionResult> Index() { var vms = await _accountServices.GetList(); return View(vms); }
4.服务的生命周期和注册选项
ASP.NET 服务生命周期:
1.Transient 瞬时
Transient 生命周期服务在他们每次请求时被建立。适合轻量级,无状态的服务。
2.Scoped 做用域
Scoped生命周期在每次请求时建立一次。
3.Singleton 单例
Singleton 生命周期服务在它们第一次请求时建立,而且每一个后续请求使用相同的实例。
服务能够用多种方式在容器中注册,除了以前的注册方法,还能够指定一个工厂,它将被用来建立须要的实例。后面会详细介绍其余的注册方法。
下面用一个简单的示例介绍每一个生命周期:
1.建立接口:
namespace MVCTest.Interfaces { public interface IOperation { /// <summary> /// 惟一标识 /// </summary> Guid OperationId { get; } } public interface IOperationTransient: IOperation { } public interface IOperationScoped : IOperation { } public interface IOperationSingleton : IOperation { } public interface IOperationInstance : IOperation { } }
2.实现类
/// <summary> /// 实现全部接口 /// </summary> public class Operation: IOperation, IOperationTransient, IOperationScoped, IOperationSingleton, IOperationInstance { public Operation() { OperationId = Guid.NewGuid(); } public Operation(Guid operationId) { if (operationId == null) { OperationId = Guid.NewGuid(); } OperationId = operationId; } public Guid OperationId { get; } }
3.注册到容器
public void ConfigureServices(IServiceCollection services) { services.AddTransient<IOperationTransient, Operation>(); services.AddScoped<IOperationScoped, Operation>(); services.AddSingleton<IOperationSingleton, Operation>(); services.AddSingleton<IOperationInstance, Operation>(); services.AddTransient<OperationServices, OperationServices>(); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); }
4.上面还注册了 OperationServices ,用来测试单例模式(单例生命周期服务中全部请求使用第一次实例化的服务)和 做用域生命周期服务在每次请求时只建立一次,无论几个地方用到实例
public class OperationServices { public IOperationTransient OperationTransient { get; } public IOperationScoped OperationScoped { get; } public IOperationSingleton OperationSingleton { get; } public IOperationInstance OperationInstance { get; } public OperationServices(IOperationTransient operationTransient, IOperationScoped operationScoped, IOperationSingleton operationSingleton, IOperationInstance operationInstance) { OperationTransient = operationTransient; OperationScoped = operationScoped; OperationSingleton = operationSingleton; OperationInstance = operationInstance; } }
5.在Controller中使用
public class OperationController : Controller { public IOperationTransient OperationTransient { get; } public IOperationScoped OperationScoped { get; } public IOperationSingleton OperationSingleton { get; } public IOperationInstance OperationInstance { get; } public OperationServices _operationServices; public OperationController(IOperationTransient operationTransient, IOperationScoped operationScoped, IOperationSingleton operationSingleton, IOperationInstance operationInstance, OperationServices operationServices) { OperationTransient = operationTransient; OperationScoped = operationScoped; OperationSingleton = operationSingleton; OperationInstance = operationInstance; _operationServices = operationServices; } // GET: Operation public ActionResult Index() { ViewBag.OperationTransient = OperationTransient; ViewBag.OperationScoped = OperationScoped; ViewBag.OperationSingleton = OperationSingleton; ViewBag.OperationInstance = OperationInstance; ViewBag._operationServices = _operationServices; return View(); } }
6.Index显示
@{ ViewData["Title"] = "Index"; } <div> <h1>Controller Operations</h1> <h2>OperationTransient: @ViewBag.OperationTransient.OperationId</h2> <h2>OperationScoped: @ViewBag.OperationScoped.OperationId</h2> <h2>OperationSingleton: @ViewBag.OperationSingleton.OperationId</h2> <h2>OperationInstance: @ViewBag.OperationInstance.OperationId</h2> </div> <div> <h1>Services Operations</h1> <h2>OperationTransient: @ViewBag._operationServices.OperationTransient.OperationId</h2> <h2>OperationScoped: @ViewBag._operationServices.OperationScoped.OperationId</h2> <h2>OperationSingleton: @ViewBag._operationServices.OperationSingleton.OperationId</h2> <h2>OperationInstance: @ViewBag._operationServices.OperationInstance.OperationId</h2> </div>
7.运行结果
能够看到,单例生命周期服务每一次请求的标识同样。做用域生命周期的服务,在一次请求中使用的同一个实例,第二次请求建立新的实例。
5.请求服务
来自HttpContext的一次ASP.NET 请求中,可用的服务是经过RequestServices集合公开的。
请求服务将你配置的服务和请求描述为应用程序的一部分。在子的对象指定依赖以后,这些知足要求的对象可经过查找RequestServices中对应的类型获得,而不是ApplicationServices。
6.设计依赖注入服务
在自定义的服务中,避免使用静态方法和直接实例化依赖的类型,而是经过依赖注入请求它。(New is Glue)
若是类有太多的依赖关系被注入时,一般代表你的类试图作的太多(违反了单一职责原则),须要转移一些职责。
一样,Controller类应该重点关注UI,所以业务逻辑和数据访问等细节应该在其余类中。
7.使用Autofac容器