创建一个asp.net core项目,可以到到startup类有两个方法
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
ConfigureServices方法:注册服务到容器中,在整个应用中都可以使用。推荐:自定义方法以Add开头
Configure方法:为应用配置请求管道.推荐:自定义方法以Use开头
这里并会深入的探讨依赖注入和IApplicationBuilder、IServiceCollection这些核心对象,这篇文章主要目的是快速的了解startup类和如何利用一些开源的项目(nopcommerce)去使用它。
nopcommerce是个优秀的开源的电商项目,应该都不会陌生,不管有没有项目中用到,但不妨碍我们去阅读学习他们优秀的地方。
一起先了解下项目结构
图
- Nop.Core 核心层 :包含领域模型、和基础设施层(缓存、仓储接口、依赖注入、对象映射mapper等)、一些其他工具里的封装
- Nop.Data 数据层:orm与数据库的一些操作,仓储实现类,领域和表的映射等
- Nop.Services 应用服务层:业务服务操作
- Plugins 插件:nop 是插件式开发 ,扩展起来很是方便
- Nop.Web 表现层:ui交互
- Nop.Web.Framework:对asp.netcore mvc 进行一些扩展和封装
在回到今天的主角startup类
我进入Nop.Web项目 打开startup类
public class Startup
{
#region Fields
private readonly IConfiguration _configuration;
private readonly IHostingEnvironment _hostingEnvironment;
#endregion
#region Ctor
public Startup(IConfiguration configuration, IHostingEnvironment hostingEnvironment)
{
_configuration = configuration;
_hostingEnvironment = hostingEnvironment;
}
#endregion
///
/// Add services to the application and configure service provider
///
/// Collection of service descriptors
public IServiceProvider ConfigureServices(IServiceCollection services)
{
return services.ConfigureApplicationServices(_configuration, _hostingEnvironment);
}
///
/// Configure the application HTTP request pipeline
///
/// Builder for configuring an application's request pipeline
public void Configure(IApplicationBuilder application)
{
application.ConfigureRequestPipeline();
}
}
是不是很简洁,可以发现nop对IServiceCollection、IApplicationBuilder进行扩展了两个方法类 分别ServiceCollectionExtensions、ApplicationBuilderExtensions,下面我们分别快速的浏览这两个类的源码
我们F12进入ConfigureApplicationServices的实现方式一步一步的查看,并且我把代码添加了基本注释
var engine = EngineContext.Create(); //创建NopEngine
var serviceProvider = engine.ConfigureServices(services, configuration);
//find startup configurations provided by other assemblies
var typeFinder = new WebAppTypeFinder(); //反射工具类
var startupConfigurations = typeFinder.FindClassesOfType();
//create and sort instances of startup configurations
var instances = startupConfigurations
.Select(startup => (INopStartup)Activator.CreateInstance(startup))
.OrderBy(startup => startup.Order);
////configure services
foreach (var instance in instances)
instance.ConfigureServices(services, configuration);
////register mapper configurations
//AddAutoMapper(services, typeFinder);
//register dependencies
RegisterDependencies(services, typeFinder);
protected virtual IServiceProvider RegisterDependencies(IServiceCollection services, ITypeFinder typeFinder)
{
var containerBuilder = new ContainerBuilder(); //Autofac
//register engine
containerBuilder.RegisterInstance(this).As().SingleInstance();
//register type finder
containerBuilder.RegisterInstance(typeFinder).As().SingleInstance();
//populate Autofac container builder with the set of registered service descriptors
containerBuilder.Populate(services);
//find dependency registrars provided by other assemblies
var dependencyRegistrars = typeFinder.FindClassesOfType();
//create and sort instances of dependency registrars
var instances = dependencyRegistrars
.Select(dependencyRegistrar => (IDependencyRegistrar)Activator.CreateInstance(dependencyRegistrar))
.OrderBy(dependencyRegistrar => dependencyRegistrar.Order);
//register all provided dependencies
foreach (var dependencyRegistrar in instances)
dependencyRegistrar.Register(containerBuilder, typeFinder);
//create service provider
_serviceProvider = new AutofacServiceProvider(containerBuilder.Build());
return _serviceProvider;
}
F12进入ConfigureRequestPipeline
EngineContext.Current.ConfigureRequestPipeline(application);
public void ConfigureRequestPipeline(IApplicationBuilder application)
{
//find startup configurations provided by other assemblies
var typeFinder = Resolve();
var startupConfigurations = typeFinder.FindClassesOfType();
//create and sort instances of startup configurations
var instances = startupConfigurations
.Select(startup => (INopStartup)Activator.CreateInstance(startup))
.OrderBy(startup => startup.Order);
//configure request pipeline
foreach (var instance in instances)
instance.Configure(application);
}
ok,到此这两个文件的源码已经过完了,我们几个类比较陌生,我们一一介绍下
- EngineContext: NopEngine的实例上下文 作用 获取创建和获取NopEngine的实例上下文的实例(涉及到的设计模式单例)
- IEngine、NopEngine: nop引擎还是很体贴的,里面封装了使用的方法如ioc 解析方法Resolve
- INopStartup :在应用程序启动时配置服务和中间件
当时我看过源码,有几处还是很巧妙的,下面我整理下,多个为什么,带着问题去看,印象更深刻,也达到了参考nop源码学习startup类的目的。
- 接口INopStartup作用?
INopStartup有两个方法ConfigureServices,Configure 跟Startup方法作用都是一样的,nop把它抽离成接口的好处,可以很方便通过反射把实现INopStartup的类查找出来,然后掉用ConfigureServices,Configure方法
//find startup configurations provided by other assemblies
var typeFinder = new WebAppTypeFinder();
var startupConfigurations = typeFinder.FindClassesOfType();
//create and sort instances of startup configurations
var instances = startupConfigurations
.Select(startup => (INopStartup)Activator.CreateInstance(startup))
.OrderBy(startup => startup.Order);
////configure services
foreach (var instance in instances)
instance.ConfigureServices(services, configuration);
//configure request pipeline
foreach (var instance in instances)
instance.Configure(application);
- nop使用Autofac作为注入框架,它是如何实现的
var containerBuilder = new ContainerBuilder();
//register engine
containerBuilder.RegisterInstance(this).As().SingleInstance();
//create service provider
_serviceProvider = new AutofacServiceProvider(containerBuilder.Build());
return _serviceProvider;
- 接口IEngine的作用?
配置startup 服务和请求管道、autofac注册和解析
IServiceProvider ConfigureServices(IServiceCollection services, IConfiguration configuration);
void ConfigureRequestPipeline(IApplicationBuilder application);
T Resolve() where T : class;
- 如何使用注入的服务?
1.我们在Nop.Services项目中添加ProductService和ProductAttributeService两个业务服务
public class ProductService : IProductService
{
public string GetProductById(int productId)
{
return "获取产品";
}
}
public class ProductAttributeService: IProductAttributeService
{
public string GetProductAttributeById(int productAttributeId)
{
return "获取产品属性";
}
}
2.我们实现IDependencyRegistrar依赖注册接口
public class DependencyRegistrar : IDependencyRegistrar
{
///
/// Register services and interfaces
///
/// Container builder
/// Type finder
public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder)
{
//file provider
builder.RegisterType().As().InstancePerLifetimeScope();
//data layer
//repositories
//services
builder.RegisterType().As().InstancePerLifetimeScope();
builder.RegisterType().As().InstancePerLifetimeScope();
}
///
/// Gets order of this dependency registrar implementation
///
public int Order => 0;
}
3.然后在homecontroller中测试,第一种构造函数注入,第二种直接使用IEngine的实例解析
#region fileds
private readonly IProductService productService;
#endregion
public HomeController(IProductService productService)
{
this.productService = productService;
}
public IActionResult Index()
{
//利用EngineContex进行解析
var productAttributeService = EngineContext.Current.Resolve();
ViewBag.result = this.productService.GetProductById(1);
ViewBag.result2 = productAttributeService.GetProductAttributeById(1);
return View();
}
然后运行查看效果
解析成功,展示的只是本分代码,实例代码上传到github上,喜欢的可以clone下来,自己调试调试,稍微调整下,可用在自己的项目中,因为nop比较成熟。
微信公众号:dotNET知音