背景
到TL有整整一年了,在這一年中公司從無到有,完成了兩個自動化系統整合項目,老板一直強調模塊化設計這個理念,可是由於團隊基礎實在是薄弱,很多規范沒辦法執行起來,以致於這兩個項目的源碼慘不忍睹,代碼寫得很亂,可以重復利用的模塊實在是太少。所以今年我主要的精力投在軟件開發規范化這一塊,以系統可擴展,模塊化,可復用性為原則。
雖然自已技術還是個小菜,但只要有思想就應該是簡單的問題,那么我從一個“用戶登錄模塊”設計開始,把這個模塊抽離出來,讓所有子系統都能夠復用這個登錄組件,並且降低模塊與主框架之間耦合。
在制造業的系統架構中,一般分為好幾個工作站,每個工作站都有一個子系統,每個子系統都有需要有用戶登錄權限控制,所以這就意味着每個子系統都要開發這個模塊,如果子系統比較多,這樣開發成本就比較高,重復代碼也很多,我分析了一下,這個用戶登錄模塊有如下特點:
-
有統一的界面風格
-
模塊根據用戶權限不同加載對應的系統子系統
-
模塊標題不一樣
我們如何設計達到組件可以重復利用?
- 復用性:讓所有子系統都能夠重復利用這個用戶登錄
- 易用性:通過配置文件根據不同的子系統設置不一樣的系統標題,子系統主框架調用組件簡單化
- 我后台做一個WCF服務作為用戶權限邏輯處理,登錄組件直接調這個服務,把用戶登錄邏輯處理層從系統框架主分離出來成一個獨立模塊,子模塊只需要引入這個組 件,做相應的配置,調用用戶驗證接口,當登錄成功時,組件內部會消息通知主框架(訂閱消息)進行啟動加載主界面,從而達到成功登錄的目的。
設計思想
設計步驟
1.設計WCF Service作后台用戶登錄驗證處理
using System.ServiceModel; using Xiaocai.Security.IDAL; namespace Xiaocai.Commons { // NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IUserService" in both code and config file together. [ServiceContract] public interface ILoginService { [OperationContract] AuthUser CheckUser(string userId, string password); } }
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.ServiceModel; using System.Text; using Xiaocai.Commons; using Xiaocai.Security.Core.Admin; using Xiaocai.Security.Core.Helpers; using Xiaocai.Security.IDAL; namespace Xiaocai.SystemHelper.Services { // NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "UserService" in code, svc and config file together. public class LoginService : ILoginService { private AuthController _controller; public LoginService() { _controller=new AuthController(); } public AuthUser CheckUser(string userId, string password) { AuthUser authUser=null; bool result=_controller.Login(userId, password); if (result) { authUser = _controller.AuthUser; } return authUser; } } }
我主要總結原理,很多細節就不多寫了,所以服務中的驗證我封裝在另一組件中,此處省略。
2.發布這個WCF服務到IIS服務器中,測試如一下是否發布成功,出現如下頁面表示已成功:
3.調用服務客戶端
using System; using System.ServiceModel; using Xiaocai.Commons; using Xiaocai.Security.IDAL; namespace Xiaocai.SystemHelper.Proxy { public class LoginHelper { public static AuthUser OnLogin(string userId,string password) { AuthUser authUser = null; using (ChannelFactory<ILoginService> channelFactory = new ChannelFactory<ILoginService>("userService")) { ILoginService proxy = channelFactory.CreateChannel(); using (proxy as IDisposable) { authUser = proxy.CheckUser(userId, password); return authUser; } } } } }
4.新建一Winform項目作為登錄組件,命名為Xiaocai.SecurityControlLibaray,把項目輸出類型改為類庫,以便子系統主框引入調用。
/****************************************************************************** * This document is the property of ShangHai TLian Agent Technology Co., Ltd. (TLAgent). * No exploitation or transfer of any information contained herein is permitted * in the absence of an agreement with Xiaocai * and neither the document nor any such information * may be released without the written consent of Xiaocai * * All right reserved by Xiaocai ******************************************************************************* * Owner: Agan * Version: 1.0.0.0 * Component:for login function * Function Description:* * Revision / History *------------------------------------------------------------------------------ * Flag Date Who Changes Description * -------- -------- --------------- ------------------------------------------- * 1 20130715 Agan File created *------------------------------------------------------------------------------ */ using System; using System.Configuration; using System.Windows.Forms; using Xiaocai.Commons; using Xiaocai.Security.IDAL; using Xiaocai.SystemHelper.Proxy; namespace Xiaocai.SecurityControlLibaray { public partial class LoginForm : Form { public LoginForm() { InitializeComponent(); } public virtual string Title { get; set; } private void LoginForm_Load(object sender, EventArgs e) { if (string.IsNullOrEmpty(ConfigurationManager.AppSettings["Title"])) { prgTitle.Text = string.Format("{0}", Title); } else { prgTitle.Text = string.Format("{0}", ConfigurationManager.AppSettings["Title"]); } txtUserId.Select(); } private void OnLogin(object sender, EventArgs e) { string userId = txtUserId.Text.Trim(); string password = txtPassWord.Text.Trim(); if (string.IsNullOrEmpty(userId) || string.IsNullOrEmpty(password)) { if (string.IsNullOrEmpty(userId)) { MessageHelper.ShowError("用戶名不能為空!"); txtUserId.Focus(); txtUserId.SelectAll(); return; } if (string.IsNullOrEmpty(password)) { MessageHelper.ShowError(@"密碼不能為空!"); txtUserId.Focus(); txtUserId.SelectAll(); return; } txtUserId.Focus(); txtUserId.SelectAll(); return; } AuthUser authUser = LoginHelper.OnLogin(userId, password); if (authUser!=null) { this.Hide(); EventService.Publish(authUser);//用戶驗證成功,發布消息,把登錄用戶傳送出去 Application.ThreadException += Application_ThreadException; } else { MessageHelper.ShowError("用戶名或密碼不正確,請聯系管理員!"); txtUserId.Focus(); txtUserId.SelectAll(); } } private static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs ex) { string message = string.Format("{0}\r\n操作發生錯誤,您需要退出系統么?", ex.Exception.Message); MessageBox.Show(message); Application.Exit(); } private void OnCancel(object sender, EventArgs e) { Application.Exit(); } private void txtPassWord_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Enter) { OnLogin(sender,e); } } private void txtUserId_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Enter) { txtPassWord.Focus(); } } } }
6.系統子框架調用
using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; using TLAgent.Commons; using TLAgent.Security.Core; using TLAgent.Security.IDAL; using TLAgent.SecurityControlLibaray; namespace TLAgent.Assembling.SecurityManager { static class Program { /// <summary> /// The main entry point for the application. /// </summary> [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); AppCore.Startup(); //Application.Run(new LoginForm()); OnLonin(); } private static void OnLonin() { //訂閱消息 EventAggregatorRepository.EventAggregator .GetEvent<ReceiveObjectEvent>() .Subscribe(SuccessLogin); Application.Run(new LoginForm()); } /// <summary> /// 登錄成功加載主窗體 /// </summary> /// <param name="objParam">用戶實體對象</param> private static void SuccessLogin(object objParam) { SecurityControlLibaray.SplashScreen.Splasher.Show(typeof(SplasherForm)); MainForm form = new MainForm(); var user = objParam as AuthUser; if (user != null) { form.AuthUser = user; } form.StartPosition = FormStartPosition.CenterScreen; form.ShowDialog(); } } }
7.配置文件中設置,如下:
<?xml version="1.0"?> <configuration> <!--WCF服務配置文件--> <system.serviceModel> <client> <endpoint address="http://localhost/SystemServices/LoginService.svc" binding="basicHttpBinding" contract="Xiaocai.Commons.ILoginService" name="userService"/> </client> </system.serviceModel> <appSettings> <add key="Title" value="用戶權限管理系統"/><!--系統標題設置--> <add key="Copyright" value=" 版權所有:小菜成長記"/><!--系統版權設置--> <!-- Database with WebService --> <add key="IsRemote" value="N"/> <add key="GlobalSessionFactory" value="Xiaocai.Security.DAL.Global.SQLServerSessionFactory,Xiaocai.Security.DAL.Global"/> <!-- Database without WCFService --> <add key="Database.SqlServerConn" value="Data Source=localhost,1433;Network Library=DBMSSOCN;Initial Catalog=SecurityDB;User ID=root;Password=12345;"/> </appSettings> </configuration>
運行子系統,顯示如下:
這個標題可以根據不同子系統功能在配置文件中作對應設置,到此基本完成這個登錄組件的設計,這個組件可以用於所有子系統的登錄界面,多次重復利用,減少開發成本。
附:因本人技術有限,也許會有更好的方法做這個模塊化設計,希望高人指點,在不斷學習中進步。