.NET MVC5简介(二)
MVCApplication---Application_Statr--RegisterRoutes--给RouteCollection添加规则,请求进到网站---X----请求地址被路由按照顺序匹配,遇到一个温和的就结束,就到对应的控制器和action。
在程序中使用log4net,首先nuget引入程序集
Logger代码
public class Logger { static Logger() { XmlConfigurator.Configure(new FileInfo(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "CfgFiles\\log4net.config"))); ILog Log = LogManager.GetLogger(typeof(Logger)); Log.Info("系统初始化Logger模块"); } private ILog loger = null; public Logger(Type type) { loger = LogManager.GetLogger(type); } /// <summary> /// Log4日志 /// </summary> /// <param name="msg"></param> /// <param name="ex"></param> public void Error(string msg = "出现异常", Exception ex = null) { Console.WriteLine(msg); loger.Error(msg, ex); } /// <summary> /// Log4日志 /// </summary> /// <param name="msg"></param> public void Warn(string msg) { Console.WriteLine(msg); loger.Warn(msg); } /// <summary> /// Log4日志 /// </summary> /// <param name="msg"></param> public void Info(string msg) { Console.WriteLine(msg); loger.Info(msg); } /// <summary> /// Log4日志 /// </summary> /// <param name="msg"></param> public void Debug(string msg) { Console.WriteLine(msg); loger.Debug(msg); } }
配置文件log4net.config
<?xml version="1.0" encoding="utf-8"?> <log4net> <!-- Define some output appenders --> <appender name="rollingAppender" type="log4net.Appender.RollingFileAppender"> <file value="log\log.txt" /> <!--追加日志内容--> <appendToFile value="true" /> <!--防止多线程时不能写Log,官方说线程非安全--> <lockingModel type="log4net.Appender.FileAppender+MinimalLock" /> <!--可以为:Once|Size|Date|Composite--> <!--Composite为Size和Date的组合--> <rollingStyle value="Composite" /> <!--当备份文件时,为文件名加的后缀--> <datePattern value="yyyyMMdd.TXT" /> <!--日志最大个数,都是最新的--> <!--rollingStyle节点为Size时,只能有value个日志--> <!--rollingStyle节点为Composite时,每天有value个日志--> <maxSizeRollBackups value="20" /> <!--可用的单位:KB|MB|GB--> <maximumFileSize value="3MB" /> <!--置为true,当前最新日志文件名永远为file节中的名字--> <staticLogFileName value="true" /> <!--输出级别在INFO和ERROR之间的日志--> <filter type="log4net.Filter.LevelRangeFilter"> <param name="LevelMin" value="INFO" /> <param name="LevelMax" value="FATAL" /> </filter> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%date [%thread] %-5level %logger - %message%newline"/> </layout> </appender> <!-- levels: OFF > FATAL > ERROR > WARN > INFO > DEBUG > ALL --> <root> <priority value="ALL"/> <level value="ALL"/> <appender-ref ref="rollingAppender" /> </root> </log4net>
public class MvcApplication : System.Web.HttpApplication { private Logger logger = new Logger(typeof(MvcApplication)); protected void Application_Start() { AreaRegistration.RegisterAllAreas();//注册区域 FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);//注册全局的Filter RouteConfig.RegisterRoutes(RouteTable.Routes);//注册路由 BundleConfig.RegisterBundles(BundleTable.Bundles);//合并压缩 ,打包工具 Combres this.logger.Info("网站启动了。。。"); } }
Area
Area请参考博客:
http://www.cnblogs.com/zgqys1980/archive/2012/08/22/2650774.html
有时候因为一个Web项目可以非常大非常复杂,多人合作开发,命名就成问题了。Area可以把项目拆分开,方便团队合作,演变到后面就可以做成插件式开发了:
MvcApplication--Application_Start--AreaRegistration.RegisterAllAreas()---其实就是把SystemAreaRegistration给注册下---添加URL地址规则--请求来了就匹配(area在普通的之前)
众所周知,MVC请求的最后是反射调用Controller+Action,信息来自于url+roy=ute,路由匹配时,只能找到Action和Controller,其实还有个步骤,扫描+存储,在bin里面找Controller的子类,然后把命名空间---类名称+方法全部存起来。
控制器类可以出现在MVC项目之外,唯一的规则就是继承自Controller,Area也可以独立开,规则是必须有个继承AreaRegistration。
public class SystemAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "System";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
name: "System_default",
url: "System/{controller}/{action}/{id}",
defaults: new { action = "Index", id = UrlParameter.Optional }
);
}
}
Razor语法:cshtml本质是一个类文件,混编了html+cs代码
写后台代码:行内--单行--多行--关键字
后台代码写html:@: 闭合的html标签 <text></text>
Html扩展控件:封装个方法,自动生成html
后端一次性完成全部内容,而且html标签闭合
我们还可以自行封装这种扩展方法
但是这个已经不流行了,就是UI改动需要重新发布
更多应该是前后分离,写前端的人是不会懂后端的写法
Layout
Masterpage--layout 默认是_layout 可以自行指定
@Styles.Render("~/Content/css") 使用样式包
@Scripts.Render("~/bundles/modernizr") 使用js包
@RenderBody() 就是页面的结合点
@RenderSection("scripts", required: false)
partialPage局部页---ascx控件,是没有自己的ACTION
@{ Html.RenderPartial("PartialPage", "这里是Html.RenderPartial"); }
@Html.Partial("PartialPage", "这里是Html.Partial")
子请求
@Html.Action("ChildAction", "Second", new { name = "Html.Action" })
@{Html.RenderAction("ChildAction", "Second", new { name = "Html.RenderAction" });}
有action,也可以传参数
[ChildActionOnly]//只能被子请求访问 不能独立访问
Asp.net MVC中Html.Partial, RenderPartial, Action,RenderAction 区别和用法请参考https://www.cnblogs.com/gesenkof99/archive/2013/06/03/3115052.html
Route
其实,路由这个东西,如果没必要的话,还是不要随便乱改了
下面是路由的一些改动:
public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); //忽略路由 正则表达式 {resource}表示变量 a.axd/xxxx resource=a pathInfo=xxxx //.axd是历史原因,最开始都是webform,请求都是.aspx后缀,IIS根据后缀转发请求;MVC出现了,没有后缀,IIS6以及更早版本,打了个补丁,把mvc的请求加上个.axd的后缀,然后这种都转发到网站----新版本的IIS已经不需要了,遇到了就直接忽略,还是走原始流程 routes.IgnoreRoute("CustomService/{*pathInfo}");//以CustomService开头,都不走路由 routes.MapRoute( name: "About", url: "About", defaults: new { controller = "Home", action = "About", id = UrlParameter.Optional } );//固定路由,/Home/About----About routes.MapRoute( name: "Test", url: "Test/{action}/{id}", defaults: new { controller = "Second", action = "Index", id = UrlParameter.Optional } );//修改控制器, routes.MapRoute( name: "Regex", url: "{controller}/{action}_{year}_{month}_{day}", defaults: new { controller = "Second", action = "Index", id = UrlParameter.Optional }, constraints: new { year = @"\d{4}", month = @"\d{2}", day = @"\d{2}" } ); //http://localhost:2017/second/Time_2019_06_13 Regex //http://localhost:2017/second/Time_2019_6_13 失败 //http://localhost:2017/second/Time?year=2019&month=6&day=13 Default //http://localhost:2017/test/Time?year=2019&month=6&day=13 Test //http://localhost:2017/test/Time_2019_06_13 失败的,只会被一个路由匹配 //常规路由,一般来说,我们不怎么扩展这个路由 routes.MapRoute( name: "Default",//路由名称,RouteCollection是key-value,key 避免重复 url: "{controller}/{action}/{id}",//正则规则:两个斜线 3个变量 defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } //默认值 没有id变量 就是UrlParameter.Optional 没有action就是index 没有controller是home ); } }
有关路由,请参考https://www.tuicool.com/articles/ne2Qfe
IOC和MVC的结合,工厂的创建和Bussiness初始化
MVC请求进来,漏油匹配,找到控制器和Action,控制器是个普通的类,Action是个普通的实例方法,是不是有一个过程,叫实例化控制器?但是现在希望通过容器来实例化这个控制器。
路由匹配后得到控制器名称--MVCHandler---ControllerBuilder.GetControllerFactory()---然后创建控制器的实例。
public class DIFactory { public static IUnityContainer GetContainer() { IUnityContainer container = null; //container.RegisterType ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap(); fileMap.ExeConfigFilename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "CfgFiles\\Unity.Config"); Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None); UnityConfigurationSection section = (UnityConfigurationSection)configuration.GetSection(UnityConfigurationSection.SectionName); container = new UnityContainer(); section.Configure(container, "Bingle"); return container; } }
不要每次都创建一个,判断是否为null
/// <summary> /// 依赖注入工厂 /// </summary> public class DIFactory { private static object _SyncHelper = new object(); private static Dictionary<string, IUnityContainer> _UnityContainerDictionary = new Dictionary<string, IUnityContainer>(); /// <summary> /// 根据containerName获取指定的container /// </summary> /// <param name="containerName">配置的containerName,默认为defaultContainer</param> /// <returns></returns> public static IUnityContainer GetContainer(string containerName) { if (!_UnityContainerDictionary.ContainsKey(containerName)) { lock (_SyncHelper) { if (!_UnityContainerDictionary.ContainsKey(containerName)) { //配置UnityContainer IUnityContainer container = new UnityContainer(); ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap(); fileMap.ExeConfigFilename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "CfgFiles\\Unity.Config"); Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None); UnityConfigurationSection configSection = (UnityConfigurationSection)configuration.GetSection(UnityConfigurationSection.SectionName); configSection.Configure(container, containerName); _UnityContainerDictionary.Add(containerName, container); } } } return _UnityContainerDictionary[containerName]; } }
ControllerBuilder有个SetControllerFactory。
public class BingleControllerFactory : DefaultControllerFactory { private Logger logger = new Logger(typeof(BingleControllerFactory)); protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) { this.logger.Warn($"{controllerType.Name}被构造..."); IUnityContainer container = DIFactory.GetContainer(); //return base.GetControllerInstance(requestContext, controllerType); return (IController)container.Resolve(controllerType); } }
/// <summary> /// 自定义的控制器实例化工厂 /// </summary> public class UnityControllerFactory : DefaultControllerFactory { private IUnityContainer UnityContainer { get { return DIFactory.GetContainer(); } } /// <summary> /// 创建控制器对象 /// </summary> /// <param name="requestContext"></param> /// <param name="controllerType"></param> /// <returns></returns> protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) { if (null == controllerType) { return null; } IController controller = (IController)this.UnityContainer.Resolve(controllerType); return controller; } /// <summary> /// 释放 /// </summary> /// <param name="controller"></param> public override void ReleaseController(IController controller) { //释放对象 //this.UnityContainer.Teardown(controller);//释放对象 Unity容器释放对象只有单例那些,瞬时的是不存在释放管理的,直接.net框架自身会即时完成对象释放 /* I wrote an article about using object lifetimes managers in Unity and their impact on disposing. If you use default TransientLifetimeManager or PerResolveLifetimeManager the Unity will even don't track existence of your objects so it can't call Dispose. The only lifetime managers which calls Dispose on resolved instances are ContainerControlledLifetimeManager (aka singleton) and HierarchicalLifetimeManager. The Dispose is called when the lifetime manager is disposed. */ base.ReleaseController(controller);// } }
步骤:
1、自己定义一个类,继承DefaultControllerFactory
2、SetFactory,实例化控制器会进到这里
3、引入第三方容器,将控制器的实例化换成容器操作
这样就完成了MVC+IOC的结合。