41

阅读nopcommerce startup源码 - 成天

 5 years ago
source link: https://www.cnblogs.com/chengtian/p/11265175.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

阅读nopcommerce startup源码

2019-07-29 17:26  李明成  阅读(712)  评论(9)  编辑  收藏

创建一个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是个优秀的开源的电商项目,应该都不会陌生,不管有没有项目中用到,但不妨碍我们去阅读学习他们优秀的地方。

一起先了解下项目结构 

449569-20190729171918011-2041264184.png
  • 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类的目的。
  1. 接口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);    
  1. nop使用Autofac作为注入框架,它是如何实现的
     var containerBuilder = new ContainerBuilder();
    //register engine
    containerBuilder.RegisterInstance(this).As<IEngine>().SingleInstance();
        
    //create service provider
    _serviceProvider = new AutofacServiceProvider(containerBuilder.Build());

     return _serviceProvider;
  1. 接口IEngine的作用? 配置startup 服务和请求管道、autofac注册和解析
IServiceProvider ConfigureServices(IServiceCollection services, IConfiguration configuration);
void ConfigureRequestPipeline(IApplicationBuilder application);
T Resolve<T>() where T : class;
  1. 如何使用注入的服务?

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();
        }

然后运行查看效果

449569-20190729171856350-540306641.png

解析成功,展示的只是本分代码,实例代码上传到github上,喜欢的可以clone下来,自己调试调试,稍微调整下,放心用在自己的项目中,因为nop已经比较成熟了。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK