通了解《系統操作日誌設計》,已基本明確我們不能通過clone的方式來做日誌的設計,因爲這樣不僅會造成的你數據庫表爆炸的情況,還大大的增加了工作量,減少了系統的可維護性。
通過思考大概清楚系統操作日誌的設計,以下是其UML圖:
通過上圖,我們可以瞭解知道該UML主要由三個表組成,其中一個主表LogSetting和兩個從表分別是LogOperation和LogSettingDetail。
那麼怎麼樣才能通過這樣的設計來現實我們的日誌功能呢?
其實一開始我就覺得通過.net的反射功能可以很簡單、很方便的實現這個功能,所以我就順着一個思路來實現她;通過反射動態的獲取Model實體的屬性,然後再根據LogSettingDetail配置來匹配所要記錄的字段信息。
先來主要的代碼吧,發現將思想用文字表達出來還是較困難的,代碼比較直接:
代碼的實現
using System; using System.Data; using System.Configuration; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; using BLL.Sys; using System.Collections.Generic; using System.Collections.Specialized; using System.Text; using System.Reflection; /// /// LogManager 的摘要說明 /// public class LogManager where T : new() { #region Constructor /// /// 日誌管理構造函數 /// public LogManager() { tableName = typeof(T).Name; Model.Sys.LogSetting model = GetLogSetting(tableName); if (model != null) { businessName = model.BusinessName; logID = model.LogID; primaryKey = model.PrimaryKey; urlTemplate = model.UrlTemplate; deleteScriptTemplate = model.DeleteScriptTemplate; updateScriptTemplate = model.UpdateScriptTemplate; } else { throw new ArgumentNullException("日誌設置爲空!"); } } /// /// /// 日誌管理構造函數 /// /// /// 表名 /// 業務名稱 public LogManager(string tableName, string businessName) { this.tableName = tableName; this.businessName = businessName; Model.Sys.LogSetting model = GetLogSetting(tableName, businessName); if (model != null) { logID = model.LogID; primaryKey = model.PrimaryKey; urlTemplate = model.UrlTemplate; deleteScriptTemplate = model.DeleteScriptTemplate; updateScriptTemplate = model.UpdateScriptTemplate; } else { throw new ArgumentNullException("日誌設置爲空!"); } } #endregion #region Properties private int logID; private string tableName; private string businessName; private string primaryKey; private string urlTemplate; private string deleteScriptTemplate; private string updateScriptTemplate; /// /// 日誌設置實體列表 /// public List<model.sys.LogSetting> LogSettingList { get { System.Web.Caching.Cache cache = HttpRuntime.Cache; List<model.sys.LogSetting> list = cache["LogSettingList"] as List<model.sys.LogSetting>; if (list != null && list.Count > 0) { return list; } else { LogSetting bll = new LogSetting(); list = bll.GetModelList(string.Empty); cache["LogSettingList"] = list; return list; } } set { System.Web.Caching.Cache cache = HttpRuntime.Cache; cache["LogSettingList"] = null; } } /// /// 日誌設置明細 /// public List<model.sys.LogSettingDetail> LogSettingDetail { get { System.Web.Caching.Cache cache = HttpRuntime.Cache; List<model.sys.LogSettingDetail> list = cache["LogSettingDetail"] as List<model.sys.LogSettingDetail>; if (list != null && list.Count > 0) { return list; } else { LogSettingDetail bll = new LogSettingDetail(); list = bll.GetModelList(string.Empty); cache["LogSettingDetail"] = list; return list; } } set { System.Web.Caching.Cache cache = HttpRuntime.Cache; cache["LogSettingDetail"] = null; } } #endregion #region Method /// /// 通過logId獲取日誌設置明細 /// /// 日誌設置編號 /// private List<model.sys.LogSettingDetail> GetLogSettingDetails(int logId) { if (logId == 0) throw new ArgumentNullException("LogID爲空"); List<model.sys.LogSettingDetail> list = new List<model.sys.LogSettingDetail>(); foreach (Model.Sys.LogSettingDetail var in LogSettingDetail) { if (var.LogID == logId) list.Add(var); } return list; } /// /// 通過tableName和businessName來獲取日誌設置對象 /// /// /// /// private Model.Sys.LogSetting GetLogSetting(string tableName, string businessName) { foreach (Model.Sys.LogSetting var in LogSettingList) { if (var.TableName.Equals(tableName, StringComparison.InvariantCultureIgnoreCase) && var.BusinessName.Equals(businessName, StringComparison.InvariantCultureIgnoreCase)) return var; } return null; } private Model.Sys.LogSetting GetLogSetting(string tableName) { foreach (Model.Sys.LogSetting var in LogSettingList) { if (var.TableName.Equals(tableName, StringComparison.InvariantCultureIgnoreCase)) return var; } return null; } /// /// 比較兩個實體,然後返回實體中每個屬性值不同的內容 /// /// /// /// public string Compare(T oldObj, T newObj) { Type objTye = typeof(T); StringBuilder sbResult = new StringBuilder(); string tableHeader = ""; tableHeader += ""; string tableRow = ""; List<model.sys.LogSettingDetail> list = GetLogSettingDetails(logID); int i = 1; foreach (Model.Sys.LogSettingDetail var in list) { PropertyInfo property = objTye.GetProperty(var.ColumnName); if (property != null && !property.IsSpecialName) { object o = property.GetValue(oldObj, null); object n = property.GetValue(newObj, null); if (!IsEqual(property.PropertyType, o, n)) { sbResult.AppendFormat(tableRow, i % 2 == 0 ? "odd" : "even", i, var.ColumnName, var.ColumnText, o, n); i++; } } } sbResult.Append("
序號 | 字段 | 名稱 | 舊值 | 新值 |
---|---|---|---|---|
{1} | {2} | {3} | {4} | {5} |
序號 | 字段 | 名稱 | 值 |
---|---|---|---|
{1} | {2} | {3} | {4} |
序號 | 字段 | 名稱 | 值 |
---|---|---|---|
{1} | {2} | {3} | {4} |
使用的場景
Model文件:
public class EmployeeModel
{
public int ID{get;set;}
public string Name{get;set;}
…
}
下面介紹如何將系統操作日誌集成到你的業務系統中
添加操作:
EmployeeBll bll = new EmployeeBll();
EmployeeModel model = new EmployeeModel();
/* model 實體經過漫長的 賦值 後… */
bll.Add(model); //添加實體
//添加系統操作記錄
//日誌 LogManager log = new LogManager();
log.Add(model);
更新操作:
EmployeeBll bll = new EmployeeBll();
EmployeeModel model = bll.GetModel(employeeID);
LogManager log = new LogManager();
EmployeeModel modelOld = log.Clone(model); //克隆EmployeeModel實體對象,這個主要是在系統操作日誌記錄時使用的
/* model 實體又經過漫長的 賦值 後… */
bll.Update(model); //更新實體
//將更新的內容寫入系統操作日誌中
log.Compare(modelOld, model); //原來的實體和賦值後的實體對比,並將更新的內容寫入系統操作日誌中
刪除操作:
在GridView的RowDeleting事件中獲取要刪除的實體
EmployeeBll bll = new EmployeeBll();
EmployeeModel model = bll.GetModel(employeeID);
bll.Delete(employeeID);
LogManager log = new LogManager();
log.Delete(model); //實體的內容記錄到日誌中
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode
pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00;
} .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }
總結:
大家可以看到代碼還是比較粗糙的,有不少的重複的代碼,下一節將會討論如何進行系統操作日誌管理。
另外如何大家有什麼意見或想法請分享提出。
本節用到的知識點:
1、泛型
2、反射
3、緩存
優點:
1、使用和集成方便,代碼量小;
2、大大提高工作效率,避免表爆炸;
缺點:
1、代碼有待優化;
2、可擴展性較差