十五、EnterpriseFrameWork框架核心类库之系统启动入口与初始化
本章内容是讲三种开发模式,web模式、Winform模式和Wcf模式的系统启动入口有什么区别,以及启动后系统初始化的内容;为什么要把这些单独提出来讲一章,因为我觉得本章非常重要,我们都知道程序中的main()函数,称之为主函数,是所有程序运行的入口;当你拿着一个程序肯定是从这个函数开始阅读,慢慢的深入了解整个程序的运行流程;而光看那些功能代码块是掌握不了系统运行时的一些机制的;
只有掌握本章的内容后,那么在以后项目中遇到的问题基本上都能直接定位,并找到产生的原因,不然你会觉得所有产生的问题都莫名其妙,不知道从哪里下手调试;项目中的配置文件也是在系统初始化中使用的,要了解配置的值到底有什么作用也必须搞清楚系统初始化的过程,还有就是框架中的很多设计思想就是基于此处产生的,最典型的就是控制器的设计,为什么控制器要如此设计,看完后应该会深有体会;
接下来我就分别对框架中的几种模式的系统启动入口进行讲解:
1.Web版系统启动入口
2.Winform版系统启动入口
3.Wcf版系统启动入口
4.启动后初始化内容
源代码目录结构:
一、Web版系统启动入口
Web版是三层结构程序,但跟wcf版又不一样,分为浏览器、Web服务器和数据库,所有代码都是部署在Web服务器上的,Windows下的常用web服务器就是IIS了;Net下的web系统启动入口通常都是使用Global.asax文件;是可以把初始化代码AppGlobal放在此文件中,但是框架为什么没有这么做,因为开始的设计就是在EFWWeb项目中是不能写任何cs代码的,想想看如果EFWWeb项目没有cs代码,那么就只有html代码和javascript代码,这样EFWWeb项目就不需要编译,要编译的代码都放在逻辑层项目中,我们发布就变得非常方便,只需拷贝更新逻辑层项目的dll和EFWWeb项目的aspx文件和js文件;如果只修改了界面那编译都不用了;
那么启动入口不在Global.asax文件中那又在哪了,框架利用了httpModules来实现;先看AppGlobalHttpModule对象的代码,继承net框架中的IHttpModule接口,在实现的Init方法中通过context.BeginRequest事件调用了AppGlobal.AppStart方法实现系统初始化;
1 /// <summary> 2 /// web系统启动调用此对象 3 /// </summary> 4 public class AppGlobalHttpModule : IHttpModule 5 { 6 7 #region IHttpModule 成员 8 9 public void Dispose() 10 { 11 AppGlobal.AppEnd(); 12 } 13 private HttpApplication _context; 14 public void Init(HttpApplication context) 15 { 16 _context = context; 17 context.BeginRequest += new EventHandler(context_BeginRequest); 18 } 19 20 void context_BeginRequest(object sender, EventArgs e) 21 { 22 AppGlobal.AppRootPath = _context.Server.MapPath("~/"); 23 AppGlobal.AppStart(AppGlobalType.Web); 24 } 25 26 #endregion 27 }
在Web.config配置文件中需要对AppGlobalHttpModule 对象进行配置;
Web版系统就是利用httpModules来实现的,再讨论一下页面想后台控制器进行Http请求的的流程;如下图,界面javascript代码利用jquery中的Ajax功能向Web服务器发送请求,Url地址指定控制器的名称和控制器方法名称;
Web服务器利用httpHandlers来接收前端的所有请求,看一下APIHttpHandler的代码,继承net框架中的IHttpHandler接口,在实现接口的ProcessRequest方法中根据url中的控制器名称和方法名称,利用反射机制创建Web控制器,并执行控制器中的方法返回结果输出到页面;
1 /// <summary> 2 /// Http请求处理对象 3 /// </summary> 4 public class APIHttpHandler : IHttpHandler, IRequiresSessionState 5 { 6 /// <summary> 7 /// 您将需要在您网站的 web.config 文件中配置此处理程序, 8 /// 并向 IIS 注册此处理程序,然后才能进行使用。有关详细信息, 9 /// 请参见下面的链接: http://go.microsoft.com/?linkid=8101007 10 /// </summary> 11 #region IHttpHandler Members 12 13 public void ProcessRequest(HttpContext context) 14 { 15 16 try 17 { 18 if (AppGlobal.IsRun == false) throw new Exception("系统未正常启动!"); 19 string sController = context.Request["controller"].ToString().Trim(); 20 string sMothod = context.Request["method"].ToString().Trim(); 21 22 23 if (!String.IsNullOrEmpty(sController) && !String.IsNullOrEmpty(sMothod)) 24 { 25 26 CmdInvoke(context, sController, sMothod); 27 } 28 else 29 { 30 context.Response.Write("error");//命令错误 31 } 32 } 33 catch (Exception err) 34 { 35 context.Response.Write("exception");//执行异常 36 //记录错误日志 37 ZhyContainer.CreateException().HandleException(err, "HISPolicy"); 38 } 39 finally 40 { 41 context.Response.End(); 42 } 43 } 44 45 public bool IsReusable 46 { 47 get 48 { 49 return false; 50 } 51 } 52 53 #endregion 54 55 private void CmdInvoke(HttpContext context, string sController, string sMothod) 56 { 57 List<Cmd_Controller> cmd = (List<Cmd_Controller>)AppGlobal.cache.GetData("cmdWebController"); 58 59 Cmd_Controller cmdC = cmd.Find(x => x.controllerName == sController); 60 if (cmdC != null) 61 { 62 Cmd_Method cmdM= cmdC.cmdMethod.Find(x => x.methodName == sMothod); 63 if (cmdM != null) 64 { 65 if (System.Configuration.ConfigurationManager.AppSettings["TurnOnLoginRight"] == "true" && cmdC.webController.GetSysLoginRight.UserId == 0) 66 { 67 context.Response.Write("nologin");//没登陆 68 } 69 else 70 { 71 //每次请求控制器必须创建一个新的数据库链接 72 //if (cmdC.webController.GetDb() == null) 73 //{ 74 EFWCoreLib.CoreFrame.DbProvider.AbstractDatabase Rdb = EFWCoreLib.CoreFrame.DbProvider.FactoryDatabase.GetDatabase(); 75 Rdb.WorkId = cmdC.webController.GetSysLoginRight.WorkId; 76 //创建数据库连接 77 cmdC.webController.BindDb(Rdb, AppGlobal.container); 78 79 if (cmdM.dbkeys != null && cmdM.dbkeys.Count > 0) 80 { 81 cmdC.webController.BindMoreDb(Rdb, "default"); 82 foreach (string dbkey in cmdM.dbkeys) 83 { 84 EFWCoreLib.CoreFrame.DbProvider.AbstractDatabase _Rdb = EFWCoreLib.CoreFrame.DbProvider.FactoryDatabase.GetDatabase(dbkey); 85 _Rdb.WorkId = cmdC.webController.GetSysLoginRight.WorkId; 86 //创建数据库连接 87 cmdC.webController.BindMoreDb(_Rdb,dbkey); 88 } 89 } 90 91 //} 92 BeginInvoke(context, cmdC.webController); 93 cmdM.methodInfo.Invoke(cmdC.webController, null); 94 EndInvoke(context, cmdC.webController); 95 } 96 } 97 else 98 { 99 context.Response.Write("error");//命令错误 100 } 101 } 102 else 103 { 104 context.Response.Write("error");//命令错误 105 } 106 }
在Web.Config配置文件中需要对APIHttpHandler对象进行配置
总结一下,Web版页面调用后台控制器的过程,利用Ajax发送Http请求被APIHttpHandler对象接收,APIHttpHandler对象解析Http请求通过反射创建控制器对象并执行方法,最后讲结果用Json格式输出到页面;
相关资料:
关于HttpHandlers和HttpModules的不同
http://www.cnblogs.com/ricksun/articles/1545491.html
httpModules 与 httpHandlers
http://www.cnblogs.com/chenlulouis/archive/2009/12/18/1626918.html
二、Winform版系统启动入口
Winform版是一个两层结构的程序,客户端和服务端,服务端就是数据库,每一个客户端运行都是独立的程序运行环境,Winform版客户端就是一个Windows窗体应用程序,所以我们直接找启动项目的Program类的Main函数;
上面AppGlobal_Init()是通过界面FrmSplash用委托的方式调用的,这是由于系统做初始化的时候需要一定时间,为了让用户觉得程序正在运行,显示一个正在加载的界面效果比较好;我们接着看AppGlobal_Init()函数代码,这里会读配置文件中值,进行判断是Winform还是WCFClient方式,本节先说Winform方式,先执行AppGlobal.AppStart(AppGlobalType.Winform)进行系统初始化,AppGlobal对象封装了所有模式的初始化代码;
到此Winform版的初始化入口就讲完了,AppGlobal对象最后一节我们再看;Winform版是三种模式中相对最容易看懂的了;
另外再讲一下Winform版的操作请求流程,系统登录进入菜单主界面后,点击菜单按钮就会打开窗体界面,我们讲讲这个过程的实现代码。
先找到按钮的点击事件,从菜单的Tag对象获取菜单的配置信息,最后调用ShowForm(winmenu)方法;
1 //点击菜单 2 void btnmenu_Click(object sender, EventArgs e) 3 { 4 btnImage = sender; 5 BaseItem baseItem = sender as BaseItem; 6 7 if (baseItem.Tag.ToString() != "" && baseItem.Tag.GetType() != typeof(BaseModule)) 8 { 9 BaseMenu menu = (BaseMenu)baseItem.Tag; 10 11 WinMenu winmenu = new WinMenu(); 12 if (Program.clienttype == "Winform") 13 { 14 winmenu.DllName = menu.DllName; 15 winmenu.FunName = menu.FunName; 16 } 17 else if (Program.clienttype == "WCFClient") 18 { 19 string[] names = menu.FunWcfName.Split(new char[] { '@' }); 20 if (names.Length == 2) 21 { 22 winmenu.DllName = names[0]; 23 winmenu.FunName = names[1]; 24 } 25 } 26 else if (Program.clienttype == "WEBClient") 27 { 28 winmenu.DllName = ""; 29 winmenu.FunName = ""; 30 } 31 32 //winmenu.DllName = menu.DllName; 33 //winmenu.FunName = menu.FunName; 34 winmenu.IsOutlookBar = menu.MenuLookBar; 35 winmenu.IsToolBar = menu.MenuToolBar; 36 winmenu.Memo = menu.Memo; 37 winmenu.MenuId = menu.MenuId; 38 winmenu.ModuleId = menu.ModuleId; 39 winmenu.Name = menu.Name; 40 winmenu.PMenuId = menu.PMenuId; 41 winmenu.SortId = menu.SortId; 42 winmenu.UrlPath = System.Configuration.ConfigurationSettings.AppSettings["WEB_serverUrl"] + menu.UrlName; 43 44 ShowForm(winmenu); 45 } 46 }
打开ShowForm()方法的代码,这段代码的意思就是找到需要打开的界面Form对象,在显示在主界面的Tab中;其中BaseController basec = ControllerCreatorFactory.ControllerCreator(menu, delegateCloseTable)是本段代码的重点,调用此方法创建控制器对象,并获取控制器包含的界面Form;
1 public void ShowForm(WinMenu menu) 2 { 3 int index = this.barMainContainer.Items.IndexOf(menu.MenuId.ToString()); 4 if (index < 0) 5 { 6 if (string.IsNullOrEmpty(menu.FunName) == false || string.IsNullOrEmpty(menu.UrlPath) == false) 7 { 8 #region winform界面 9 List<DockContainerItem> listitem = new List<DockContainerItem>(); 10 11 CloseTab delegateCloseTable = delegate() 12 { 13 foreach (DockContainerItem item in listitem) 14 barMainContainer.CloseDockTab(item); 15 }; 16 17 18 19 Form form = null; 20 if (Program.clienttype == "Winform") 21 { 22 BaseController basec = ControllerCreatorFactory.ControllerCreator(menu, delegateCloseTable); 23 if (string.IsNullOrEmpty(menu.FunName) == false) 24 { 25 string[] funs = menu.FunName.Split(new char[] { '|' }); 26 if (funs.Length == 1) 27 form = (Form)basec.DefaultView; 28 else 29 form = (Form)basec.iBaseView[funs[1]]; 30 }else 31 form = (Form)basec.DefaultView; 32 } 33 else if (Program.clienttype == "WCFClient") 34 { 35 //string dllfile = menu.DllName; 36 //string controllername = menu.FunName.Split(new char[] { '|' })[0]; 37 BaseWCFClientController basec = ControllerCreatorFactory.WCFControllerCreator(menu, delegateCloseTable); 38 if (string.IsNullOrEmpty(menu.FunName) == false) 39 { 40 string[] funs = menu.FunName.Split(new char[] { '|' }); 41 if (funs.Length == 1) 42 form = (Form)basec.DefaultView; 43 else 44 form = (Form)basec.iBaseView[funs[1]]; 45 } 46 else 47 form = (Form)basec.DefaultView; 48 } 49 50 if (form != null) 51 { 52 barMainContainer.BeginInit(); 53 int displayWay = EFWCoreLib.WinformFrame.Common.CustomConfigManager.GetDisplayWay();//显示方式 0 标准 1全屏 54 if (displayWay == 1) 55 form.Dock = DockStyle.Fill; 56 form.Size = new Size(1000, 600); 57 form.FormBorderStyle = FormBorderStyle.None; 58 form.TopLevel = false; 59 if (this.barMainContainer.Width > form.Width) 60 { 61 form.Location = new Point((barMainContainer.Width - form.Width) / 2, 0); 62 } 63 else 64 form.Location = new Point(0, 0); 65 form.Show(); 66 67 PanelDockContainer panelDockMain = new PanelDockContainer(); 68 panelDockMain.Dock = DockStyle.Fill; 69 panelDockMain.Controls.Add(form); 70 panelDockMain.Location = new System.Drawing.Point(3, 28); 71 panelDockMain.Style.Alignment = System.Drawing.StringAlignment.Center; 72 panelDockMain.Style.GradientAngle = 90; 73 panelDockMain.BackColor = Color.FromArgb(227, 239, 255); 74 panelDockMain.AutoScroll = true; 75 76 77 78 DockContainerItem item = new DockContainerItem(form.Text); 79 item.Text = menu.Name; 80 item.Name = menu.MenuId.ToString(); 81 item.Control = panelDockMain; 82 item.Visible = true; 83 item.Tag = form;//绑定界面对象 84 item.Image = GetButtonImage(btnImage); 85 86 item.VisibleChanged += new EventHandler(item_VisibleChanged); 87 //this.barMainContainer.Controls.Add(panelDockMain); 88 this.barMainContainer.Items.Add(item); 89 this.barMainContainer.SelectedDockContainerItem = item; 90 91 92 listitem.Add(item); 93 94 95 barMainContainer.EndInit(); 96 this.barMainContainer.Show(); 97 } 98 #endregion 99 } 100 } 101 else 102 { 103 this.barMainContainer.SelectedDockContainerItem = (DockContainerItem)this.barMainContainer.Items[index]; 104 string formname = ((DockContainerItem)this.barMainContainer.Items[index]).Tag.GetType().Name; 105 if (formname == "FrmWebBrowser") 106 { 107 EFWCoreLib.WinformFrame.WebBrowser.IfrmWebBrowserView webbrowser = (EFWCoreLib.WinformFrame.WebBrowser.IfrmWebBrowserView)((DockContainerItem)this.barMainContainer.Items[index]).Tag; 108 webbrowser.NavigateUrl();//重新加载网址 109 } 110 } 111 112 }
接着我们将ControllerCreatorFactory创建控制器的工厂类,所以这里使用了一个工厂模型用来创建不同的控制器,包含Winform界面控制器、WcfClient界面控制器和浏览器界面控制器;我们这里先看创建Winform界面控制器的实现方法InstanceController();此方法通过菜单配置的控制器名称,利用反射机制找到控制器与控制器自定义标签定义的界面Form,这样主界面就可以显示此界面Form在Tab页中;
1 public override Object InstanceController(CloseTab close) 2 { 3 try 4 { 5 6 //加载类库 7 Assembly assembly = null; 8 assembly = Assembly.LoadFrom(AppGlobal.AppRootPath + "\\" + dllfile); 9 10 //获得控制器类(型) 11 Type type = assembly.GetType(controllername, false, true); 12 13 MenuAttribute[] menuAttribute = (MenuAttribute[])type.GetCustomAttributes(typeof(MenuAttribute), true); 14 ViewAttribute[] viewAttribute = (ViewAttribute[])type.GetCustomAttributes(typeof(ViewAttribute), true); 15 //ServiceAttribute[] serviceAttribute = (ServiceAttribute[])type.GetCustomAttributes(typeof(ServiceAttribute), true); 16 17 if (menuAttribute.Length > 0) 18 { 19 Dictionary<string, IBaseView> viewDic = new Dictionary<string, IBaseView>(); 20 //Dictionary<string, SoapHttpClientProtocol> serviceDic = new Dictionary<string, SoapHttpClientProtocol>(); 21 BaseController controller = (BaseController)System.Activator.CreateInstance(type); 22 23 EFWCoreLib.CoreFrame.DbProvider.AbstractDatabase Rdb = EFWCoreLib.CoreFrame.DbProvider.FactoryDatabase.GetDatabase(); 24 Rdb.WorkId = controller.GetSysLoginRight.WorkId; 25 controller.BindDb(Rdb, AppGlobal.container); 26 27 controller.closeTab = close;//关闭窗口 28 29 for (int index = 0; index < viewAttribute.Length; index++) 30 { 31 if (viewAttribute[index].ViewType == null) 32 { 33 if (string.IsNullOrEmpty(viewAttribute[index].DllName)) 34 { 35 continue; 36 } 37 Assembly _assembly = Assembly.LoadFrom(AppGlobal.AppRootPath + "\\" + viewAttribute[index].DllName); 38 viewAttribute[index].ViewType = _assembly.GetType(viewAttribute[index].ViewTypeName, false, true); 39 } 40 IBaseView view = (IBaseView)System.Activator.CreateInstance(viewAttribute[index].ViewType); 41 if (viewAttribute[index].DefaultView) controller._defaultView = view; 42 if (index == 0 && viewAttribute.ToList().FindIndex(x => x.DefaultView == true) == -1) controller._defaultView = view; 43 viewDic.Add(viewAttribute[index].ViewType.Name, view); 44 } 45 //for (int index = 0; index < serviceAttribute.Length; index++) 46 //{ 47 // SoapHttpClientProtocol service = (SoapHttpClientProtocol)System.Activator.CreateInstance(serviceAttribute[index].ServiceType); 48 // serviceDic.Add(serviceAttribute[index].ServiceType.Name, service); 49 //} 50 51 controller.iBaseView = viewDic; 52 //controller.service = serviceDic; 53 54 controller.Init();//初始化 55 return controller; 56 } 57 58 return null; 59 } 60 catch (Exception err) 61 { 62 throw err; 63 } 64 }
总结一下Winform版调用控制器的过程,点击菜单按钮根据菜单的配置信息,ViewCreator对象创建对应的控制器,显示控制器自定义标签包含的界面;接着界面可以通过InvokeController方法执行控制器中的代码;
三、Wcf版系统启动入口
Wcf版是三层结构的程序,包括客户端、中间件和数据库,其中客户端的启动入口与上面Winform版的差不多,实现的方式一样的,用配置参数ClientType进行区分;重点讲一下中间件WCFHosting服务主机的启动入口;
查看WCFHosting程序点击启动按钮的事件代码,代码打开一个WCF的ServiceHost,绑定WCFHandlerService对象;
1 private void StartAppHost() 2 { 3 Loader.hostwcfclientinfoList = new HostWCFClientInfoListHandler(BindGridClient); 4 Loader.hostwcfMsg = new HostWCFMsgHandler(AddMsg); 5 6 mAppHost = new ServiceHost(typeof(WCFHandlerService)); 7 8 ServiceMetadataBehavior smb = mAppHost.Description.Behaviors.Find<ServiceMetadataBehavior>(); 9 if (smb == null) 10 { 11 mAppHost.Description.Behaviors.Add(new ServiceMetadataBehavior()); 12 } 13 mAppHost.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexTcpBinding(), "mex"); 14 15 mAppHost.Open(); 16 17 AddMsg(DateTime.Now, "WCF主机启动完成"); 18 19 }
接着查看WCFHandlerService服务对象代码,执行构造函数调用了 AppGlobal.AppStart(AppGlobalType.WCF)进行系统初始化;
1 public WCFHandlerService() 2 { 3 //mCallBackList = new List<IClientService>(); 4 Loader.ShowHostMsg(DateTime.Now, "WCFHandlerService服务正在初始化..."); 5 AppGlobal.AppRootPath = System.Windows.Forms.Application.StartupPath + "\\"; 6 AppGlobal.AppStart(AppGlobalType.WCF); 7 Loader.ShowHostMsg(DateTime.Now, "WCFHandlerService服务初始化完成"); 8 9 if (Convert.ToInt32(HostSettingConfig.GetValue("heartbeat")) == 1) 10 Loader.StartListenClients(); 11 }
服务主机的启动过程就是这样,接着我们再讲一下WCF版的客户端向WCFHosting中间件请求的流程;先看客户端控制器调用Wcf服务的代码,是通过InvokeWCFService方法指定后台wcf控制器的名称和方法名;
接着看InvokeWCFService方法的实现代码,通过调用WCFService对象的ProcessRequest来执行WCFHandlerService服务;这段代码利用wcf服务实现了客户端和中间件之间的通讯;
1 public virtual Object InvokeWCFService(string controller, string method, string jsondata) 2 { 3 4 string retJson; 5 using (var scope = new OperationContextScope(WCFService as IContextChannel)) 6 { 7 var router = System.ServiceModel.Channels.MessageHeader.CreateHeader("routerID", myNamespace, AppGlobal.cache.GetData("routerID").ToString()); 8 OperationContext.Current.OutgoingMessageHeaders.Add(router); 9 retJson = WCFService.ProcessRequest(ClientID, controller, method, jsondata); 10 } 11 object Result = JavaScriptConvert.DeserializeObject(retJson); 12 int ret = Convert.ToInt32(((Newtonsoft.Json.JavaScriptObject)(Result))["flag"]); 13 string msg = ((Newtonsoft.Json.JavaScriptObject)(Result))["msg"].ToString(); 14 if (ret == 1) 15 { 16 throw new Exception(msg); 17 } 18 else 19 { 20 return ((Newtonsoft.Json.JavaScriptObject)(Result))["data"]; 21 } 22 }
再接着看WCFHandlerService服务对于ProcessRequest方法的实现,通过参数传递的控制器名称和方法名称利用反射机制执行wcf控制器中的代码;
1 public string ProcessRequest(string mProxyID, string controller, string method, string jsondata) 2 { 3 try 4 { 5 if (Convert.ToInt32(HostSettingConfig.GetValue("debug")) == 1) 6 Loader.ShowHostMsg(DateTime.Now, "客户端[" + mProxyID + "]正在执行:" + controller + "." + method + "(" + jsondata + ")"); 7 string retJson = Loader.ProcessRequest(mProxyID, controller, method, jsondata); 8 return "{\"flag\":0,\"msg\":" + "\"\"" + ",\"data\":" + retJson + "}"; 9 } 10 catch (Exception err) 11 { 12 if (err.InnerException == null) 13 { 14 Loader.ShowHostMsg(DateTime.Now, "客户端[" + mProxyID + "]执行失败:" + controller + "." + method + "(" + jsondata + ")\n错误原因:" + err.Message); 15 return "{\"flag\":1,\"msg\":" + "\"" + err.Message + "\"" + "}"; 16 } 17 else 18 { 19 Loader.ShowHostMsg(DateTime.Now, "客户端[" + mProxyID + "]执行失败:" + controller + "." + method + "(" + jsondata + ")\n错误原因:" + err.InnerException.Message); 20 return "{\"flag\":1,\"msg\":" + "\"" + err.InnerException.Message + "\"" + "}"; 21 } 22 } 23 }
1 public static string ProcessRequest(string mProxyID, string controller, string method, string jsondata) 2 { 3 if (remoteLoaderDic[mProxyID] != null) 4 { 5 RemoteLoaderController mRemoteLoader = remoteLoaderDic[mProxyID]; 6 return mRemoteLoader.InvokeController(wcfClientDic[mProxyID], controller, method, jsondata); 7 } 8 return ""; 9 }
1 public string InvokeController(WCFClientInfo clientinfo, string controller, string method, string jsondata) 2 { 3 try 4 { 5 //ViewHandler = _viewH; 6 object[] paramValue = null;//jsondata? 7 object retObj = null; 8 string retJson = null; 9 10 List<Cmd_WCFController> cmd = (List<Cmd_WCFController>)AppGlobal.cache.GetData("cmdWcfController"); 11 Cmd_WCFController cmdC = cmd.Find(x => x.controllerName == controller); 12 if (cmdC != null) 13 { 14 cmdC.wcfController.ParamJsonData = jsondata; 15 cmdC.wcfController.ClientInfo = clientinfo; 16 Cmd_WCFMethod cmdM = cmdC.cmdMethod.Find(x => x.methodName == method); 17 if (cmdM != null) 18 { 19 if (controller == "LoginController" || controller == "TestWCFController") 20 { 21 } 22 else 23 { 24 if (System.Configuration.ConfigurationManager.AppSettings["TurnOnLoginRight"] == "true" && clientinfo.LoginRight == null) 25 { 26 //context.Response.Write("nologin");//没登陆 27 throw new Exception("没登陆"); 28 } 29 } 30 31 //每次请求控制器必须创建一个新的数据库链接 32 EFWCoreLib.CoreFrame.DbProvider.AbstractDatabase Rdb = EFWCoreLib.CoreFrame.DbProvider.FactoryDatabase.GetDatabase(); 33 Rdb.WorkId = cmdC.wcfController.GetSysLoginRight.WorkId; 34 //创建数据库连接 35 cmdC.wcfController.BindDb(Rdb, AppGlobal.container); 36 37 if (cmdM.dbkeys != null && cmdM.dbkeys.Count > 0) 38 { 39 cmdC.wcfController.BindMoreDb(Rdb, "default"); 40 foreach (string dbkey in cmdM.dbkeys) 41 { 42 EFWCoreLib.CoreFrame.DbProvider.AbstractDatabase _Rdb = EFWCoreLib.CoreFrame.DbProvider.FactoryDatabase.GetDatabase(dbkey); 43 _Rdb.WorkId = cmdC.wcfController.GetSysLoginRight.WorkId; 44 //创建数据库连接 45 cmdC.wcfController.BindMoreDb(_Rdb, dbkey); 46 } 47 } 48 retObj = cmdM.methodInfo.Invoke(cmdC.wcfController, paramValue); 49 } 50 else 51 { 52 //context.Response.Write("error");//命令错误 53 throw new Exception("控制器[" + cmdC.controllerName + "],没有[" + cmdM.methodName + "]方法!"); 54 } 55 } 56 else 57 { 58 //context.Response.Write("error");//命令错误 59 throw new Exception("没有控制器[" + cmdC.controllerName + "]"); 60 } 61 if (retObj != null) 62 retJson = retObj.ToString(); 63 return retJson; 64 } 65 catch (Exception ex) 66 { 67 if (ex.InnerException == null) 68 throw new Exception(ex.Message); 69 else 70 throw new Exception(ex.InnerException.Message); 71 } 72 }
总结一下wcf版的客户端向中间件发送请求的过程,客户端控制器利用WCFHandlerService服务通讯中间件,并发送需要执行的控制器名称和方法,中间件WCFHandlerService服务接收请求并根据参数利用反射调用wcf控制器的代码返回执行结果;
四、启动后初始化内容
通过上面对系统启动入口的讲解我们知道所有模式的初始化内容都是封装在AppGlobal对象中的,初始化的类型包括四种Web、Winform、WCF、WCFClient;WCFClient类型最简单,只要创建EntLib中的缓存对象;另外三种类型内容包括:
1)EntLib中的Unity对象、Cache对象
2)定义定时任务、委托代码对象
3)配置信息,BusinessDll、IsSaas、EFWUnity
4)Web类型加载实体、web控制器、WebService服务
5)WCF类型加载实体、wcf控制器
6)Winform类型加载实体
7)初始化定时任务、委托代码和系统启动停止扩展
8)测试数据库连接
再就是对启动的过程进行了日志记录,还有整个过程在系统中只能执行一次;
public enum AppGlobalType { Web,Winform,WCF,WCFClient }
AppGlobal对象代码
1 /// <summary> 2 /// 系统启动前初始化环境 3 /// </summary> 4 public class AppGlobal 5 { 6 /// <summary> 7 /// 应用程序根目录 8 /// </summary> 9 public static string AppRootPath; 10 11 /// <summary> 12 /// 逻辑层程序集 13 /// </summary> 14 public static List<string> BusinessDll; 15 16 /// <summary> 17 /// 是否启动成功 18 /// </summary> 19 public static bool IsRun = false; 20 /// <summary> 21 /// 是否Saas模式,where条件是否加workid 22 /// </summary> 23 public static bool IsSaas = false; 24 25 /// <summary> 26 /// Unity对象容器 27 /// </summary> 28 public static IUnityContainer container; 29 /// <summary> 30 /// 企业库缓存 31 /// </summary> 32 public static ICacheManager cache; 33 34 /// <summary> 35 /// 定制任务 36 /// </summary> 37 public static List<TimingTask> taskList; 38 39 /// <summary> 40 /// 委托代码 41 /// </summary> 42 public static Hashtable codeList; 43 44 45 private static bool _isCalled = false; 46 47 private static object locker = new object(); 48 49 public static void AppStart(AppGlobalType appType) 50 { 51 lock (locker) 52 { 53 if (_isCalled == false) 54 { 55 try 56 { 57 WriterLog("--------------------------------"); 58 WriterLog("应用开始启动!"); 59 if (appType == AppGlobalType.WCFClient) 60 { 61 cache = ZhyContainer.CreateCache(); 62 } 63 else 64 { 65 //初始化静态变量 66 container = ZhyContainer.CreateUnity(); 67 cache = ZhyContainer.CreateCache(); 68 taskList = new List<TimingTask>(); 69 codeList = new Hashtable(); 70 //加载引用业务程序集 71 BusinessDll = new List<string>(); 72 string[] dllfiles = System.Configuration.ConfigurationManager.AppSettings["BusinessDll"].Split(new char[] { '|' }); 73 BusinessDll = dllfiles.ToList(); 74 75 IsSaas = System.Configuration.ConfigurationManager.AppSettings["IsSaas"] == "true" ? true : false; 76 //加载Unity配置 77 var fileMap = new ExeConfigurationFileMap { ExeConfigFilename = AppRootPath + "Config/EFWUnity.config" }; 78 System.Configuration.Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None); 79 var unitySection = (UnityConfigurationSection)configuration.GetSection("unity"); 80 ZhyContainer.AddUnity(unitySection);//判断EntLib的路径对不对 81 82 83 if (BusinessDll.Count > 0) 84 { 85 switch (appType) 86 { 87 case AppGlobalType.Web: 88 //加载Entity自定义配置 89 EFWCoreLib.CoreFrame.BusinessArchitecture.Entity_Attribute.LoadEntityAttribute(AppRootPath+"bin/",BusinessDll, cache); 90 //加载WebController到缓存(此地方可能存在并发BUG) 91 EFWCoreLib.WebFrame.Controller.Cmd_Controller.LoadCmdController(BusinessDll, cache); 92 EFWCoreLib.WebFrame.HttpHandler.WebServiceInvoker.LoadWebService(BusinessDll, cache); 93 break; 94 case AppGlobalType.WCF: 95 //加载Entity自定义配置 96 EFWCoreLib.CoreFrame.BusinessArchitecture.Entity_Attribute.LoadEntityAttribute(AppRootPath, BusinessDll, cache); 97 EFWCoreLib.WCFHandler.Cmd_WCFController.LoadCmdController(BusinessDll, cache); 98 break; 99 case AppGlobalType.Winform: 100 //加载Entity自定义配置 101 EFWCoreLib.CoreFrame.BusinessArchitecture.Entity_Attribute.LoadEntityAttribute(AppRootPath, BusinessDll, cache); 102 break; 103 } 104 } 105 //初始化Web定制任务 106 MultiTask.Init(container, taskList);//任务 107 //是否开启Web控制器请求权限认证 108 109 //扩展Global,网站程序启动、停止可自定义代码 110 GlobalExtend.StartInit(); 111 //初始化委托代码 112 ExecuteFun.Init(container, codeList);//执行函数 113 114 //测试数据库连接 115 EFWCoreLib.CoreFrame.DbProvider.FactoryDatabase.TestDbConnection(); 116 117 } 118 _isCalled = true; 119 WriterLog("应用启动成功!"); 120 WriterLog("--------------------------------"); 121 122 IsRun = true; 123 } 124 catch(Exception err) 125 { 126 AppGlobal.WriterLog("应用启动失败!"); 127 AppGlobal.WriterLog("--------------------------------"); 128 throw err; 129 } 130 } 131 } 132 } 133 134 public static void AppStart() 135 { 136 AppStart(AppGlobalType.WCF); 137 } 138 139 public static void AppEnd() 140 { 141 GlobalExtend.EndInit(); 142 } 143 144 public static void WriterLog(string info) 145 { 146 info = "时间:" + DateTime.Now.ToString() + "\t\t" + "内容:" + info + "\r\n"; 147 File.AppendAllText(AppRootPath + "startlog.txt", info); 148 } 149 150 }
五、系统启动入口与操作请求流程