建立一個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
/// <summary>
/// Add services to the application and configure service provider
/// </summary>
/// <param name="services">Collection of service descriptors</param>
public IServiceProvider ConfigureServices(IServiceCollection services)
{
return services.ConfigureApplicationServices(_configuration, _hostingEnvironment);
}
/// <summary>
/// Configure the application HTTP request pipeline
/// </summary>
/// <param name="application">Builder for configuring an application's request pipeline</param>
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<INopStartup>();
//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<IEngine>().SingleInstance();
//register type finder
containerBuilder.RegisterInstance(typeFinder).As<ITypeFinder>().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<IDependencyRegistrar>();
//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<ITypeFinder>();
var startupConfigurations = typeFinder.FindClassesOfType<INopStartup>();
//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);
}
到此這兩個檔案的原始碼已經過完了,覺得很核心的幾個物件
- 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<INopStartup>();
//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<IEngine>().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<T>() 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
{
/// <summary>
/// Register services and interfaces
/// </summary>
/// <param name="builder">Container builder</param>
/// <param name="typeFinder">Type finder</param>
public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder)
{
//file provider
builder.RegisterType<NopFileProvider>().As<INopFileProvider>().InstancePerLifetimeScope();
//data layer
//repositories
//services
builder.RegisterType<ProductAttributeService>().As<IProductAttributeService>().InstancePerLifetimeScope();
builder.RegisterType<ProductService>().As<IProductService>().InstancePerLifetimeScope();
}
/// <summary>
/// Gets order of this dependency registrar implementation
/// </summary>
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<IProductAttributeService>();
ViewBag.result = this.productService.GetProductById(1);
ViewBag.result2 = productAttributeService.GetProductAttributeById(1);
return View();
}
然後執行檢視效果
解析成功,展示的只是本分程式碼,例項程式碼上傳到github上,喜歡的可以clone下來,自己除錯除錯,稍微調整下,放心用在自己的專案中,因為nop已經比較成熟了。