前面分享了.net core program类的启动过程已经源代码介绍,这里将继续讲startup类中的两个约定方法,一个是configureservices,这个方法是用来写我们应用程序所依赖的组件。另一个configure,它是我们mvc请求的中间件方法,也就是我们每个请求来要执行的过程都可以写在这个方法里面。
      为什么说startup类中的两个方法是基于约定的呢?其实是这样的,在.net core program类main方法中有个调用了run方法这个方法从iservicecollection容器中拿到一个istartup类型的实例然后调用了istartup中定义的两个方法法,如果我们的startup类是实现了这个接口的类 那么就不是基于约定了,直接就可以使用,但是我们发现在vs给我们生成的startup类并没有实现任何接口,所以就不会是istartup类型,那么内部是如何去做的呢?  其实是这样的,在注册startup实例的时候还有个类型叫做conventionbasedstartup从名称上解读这个类就是转换为基础的startup,其实却是也是这样的,这个类中是实现了istartup接口,它的两个方法中分别调用了各自的对用委托,这些委托实际执行的就是我们startup类中定义的两个方法,请看源代码:

 public class conventionbasedstartup : istartup
    {
        private readonly startupmethods _methods;

        public conventionbasedstartup(startupmethods methods)
        {
            _methods = methods;
        }

        public void configure(iapplicationbuilder app)
        {
            try
            {
                _methods.configuredelegate(app);
            }
            catch (exception ex)
            {
                if (ex is targetinvocationexception)
                {
                    exceptiondispatchinfo.capture(ex.innerexception).throw();
                }
                throw;
            }
        }

        public iserviceprovider configureservices(iservicecollection services)
        {
            try
            {
                return _methods.configureservicesdelegate(services);
            }
            catch (exception ex)
            {
                if (ex is targetinvocationexception)
                {
                    exceptiondispatchinfo.capture(ex.innerexception).throw();
                }
                throw;
            }
        }
    }

  现在startup类的方法说清楚了,我们具体来说说方法中的内容,首先说configureservices(iservicecollection services),这个方法的参数是约定好的,不能随意改变,里面的iservicecollection接口其实就是我们依赖注入的容器,说的再直白一点就是我们整个mvc所需要的实例都由iservicecollection所管理,iservicecollection有几个重要的扩展方法,他们都是定义在servicecollectionserviceextensions静态类中,addtransient方法,表示用这个方法添加到iservicecollection容器的实例在需要注入的实例中都是一个全新的实例,addscoped方法,这个方法表示在一次请求的生命周期内共用一个实例,addsingleton方法,这个方法表示整个程序共用一个实例,例如日志服务,iconfiguration服务等都属于典型singleton。请看例子:

 public class startup
    {
        public void configureservices(iservicecollection services)
        {
            services.addtransient<transientservice>();
            services.addtransient<servicedemo1>();
            services.addtransient<servicedemo2>();
        }

        public void configure(iapplicationbuilder app, servicedemo1 demo1, servicedemo2 demo2 )
        {
            demo1.test();
            demo2.test();
            app.run(async (httpcontext context) =>
            {
              await  context.response.writeasync("test successd");
            });
        }
    }
    public class transientservice
    {
        private int _updatecount;
        public int getupdatecount()
        {   
this._updatecount = this._updatecount + 1; return this._updatecount; } } public class servicedemo1 { private readonly transientservice _service; public servicedemo1(transientservice service) { _service = service; } public void test() { console.writeline($"我是demo1的计数:{this._service.getupdatecount()}"); } } public class servicedemo2 { private readonly transientservice _service; public servicedemo2(transientservice service) { _service = service; } public void test() { console.writeline($"我是demo2的计数:{this._service.getupdatecount()}"); } }

  上面的例子中会产生一下结果,可以看得出来这两个注入的transientservice都是全新的实例

如果我们稍微改变一下注入的方法,将原本的 services.addtransient<transientservice>();改成services.addscoped<transientservice>();就会产生如下结果:

这个能说明什么呢,我们有两次注入  这个就表示transientservice保持了之前demo1的状态  demo1和demo2是可以共用这个实例来传输数据的,addsingleton方法理解起来比较简单就不过多絮叨了,上面已经说明。

接下来再来说说startup类中的configure方法,configure方法中的参数是可以变化的,也就是说你可以用依赖注入的方法在参数中注入你想要注入的实例,前面说了 这个方法是我们请求的中间件方法,这个方法中会整合我们注入的iapplicationbuilder 中调用的各种use方法中定义的中间件  并不是说这里面定义的代码每次请求都会被执行,这个概念一定要搞清楚,configure方法只会在启动的时候执行一次,后面就不会再执行了,configure方法只是让我们可以定义整个mvc的处理请求的执行顺序,具体的可以看看官方的文档

其实中间件都是由iapplicationbuilder 所管理的applicationbuilder类实现了iapplicationbuilder 接口中的方法,看到applicationbuilder中的源代码中有个属性_components 它是一个ilist<func<requestdelegate, requestdelegate>>类型的委托容器,容器中的委托就是请求过来需要执行的中间件委托,当你在configure方法中调用app.usexxx的时候就会被注册到这个容器中去,然后请求过来就按照顺序执行容器中的每一个委托,所以这里就解释了前面说的configure方法只会被执行一次的说法。下面也贴一下applicationbuilder类的源代码:

 public class applicationbuilder : iapplicationbuilder
    {
        private readonly ilist<func<requestdelegate, requestdelegate>> _components = new list<func<requestdelegate, requestdelegate>>();

        public iserviceprovider applicationservices
        {
            get
            {
                return getproperty<iserviceprovider>(constants.builderproperties.applicationservices);
            }
            set
            {
                setproperty(constants.builderproperties.applicationservices, value);
            }
        }

        public ifeaturecollection serverfeatures => getproperty<ifeaturecollection>(constants.builderproperties.serverfeatures);

        public idictionary<string, object> properties
        {
            get;
        }

        public applicationbuilder(iserviceprovider serviceprovider)
        {
            properties = new dictionary<string, object>(stringcomparer.ordinal);
            applicationservices = serviceprovider;
        }

        public applicationbuilder(iserviceprovider serviceprovider, object server)
            : this(serviceprovider)
        {
            setproperty(constants.builderproperties.serverfeatures, server);
        }

        private applicationbuilder(applicationbuilder builder)
        {
            properties = new copyonwritedictionary<string, object>(builder.properties, stringcomparer.ordinal);
        }

        private t getproperty<t>(string key)
        {
            if (!properties.trygetvalue(key, out object value))
            {
                return default(t);
            }
            return (t)value;
        }

        private void setproperty<t>(string key, t value)
        {
            properties[key] = value;
        }

        public iapplicationbuilder use(func<requestdelegate, requestdelegate> middleware)
        {
            _components.add(middleware);
            return this;
        }

        public iapplicationbuilder new()
        {
            return new applicationbuilder(this);
        }

        public requestdelegate build()
        {
            requestdelegate requestdelegate = delegate (httpcontext context)
            {
                context.response.statuscode = 404;
                return task.completedtask;
            };
            foreach (func<requestdelegate, requestdelegate> item in _components.reverse())
            {
                requestdelegate = item(requestdelegate);
            }
            return requestdelegate;
        }
    }

好啦,这篇关于startup类就算介绍完成了,下篇开始正式介绍mvc