近期接手一个项目,其中一个功能是显示查验报告,查验标的有60种之多,特性如下:
1、原有的数据库设计者,为每种标的建了一个表,每个表中的字段完全不相同;
2、有些表的数据在输出时,需要进行一些格式化,而有些表的数据则不需要;
3、有的报表除了默认表的数据外,还需要添加一些额外的数据;
首先,每个表对应一个报表,我们可以视其为60个报表对象,它们有一些共同的属性(如:报表编号)和方法(GetReport),我们将它们抽取出来,定义成为接口IReport;
其次,为了各种数据表不同的处理方式,我们采用委托来达到这个目标,在有委托方法传入时,调用委托方法处理数据。
解决方案:
1、建立报告模板,使用标签替换的方式将数据替换到文本中,并在aspx页面上输出。
2、接口设计
View Code
namespace TableInterface { /// <summary> /// 表数据读取 /// 报告数据读取 /// </summary> public interface IReport { string DBName { get; set; } string TableName { get; set; } string PrtNum { get; set; } string PhNum { get; set; } string SyNum { get; set; } string WtNum { get; set; } /// <summary> /// 获取检验报告 /// </summary> /// <returns></returns> string GetReport(); /// <summary> /// 获取数据的JSON串 /// </summary> /// <returns></returns> string GetDataJson(); } }
3、抽象类设计,实现IReport接口
View Code
using System.Collections.Generic; using Blackice.Utility; using TableInterface; namespace ReportCreater { /// <summary> /// #Report使用方法 /// 1、创建一个新的项目,以grou表名做为项目名称 /// 2、新建Report类,引用ReportCreater.DLL,TableInterface.DLL /// 4、Report 继承抽象类 ReportCreater.Report /// 5、新建实体类 Table /// 6、如果有自定义数据,请重写 GetReport 方法 /// 7、标签规则 /// IList数据源的标签格式为 {$字段名称+行或列编号},如{$prtnum1}、{$prtnum2} /// DataTable数据源的标签格式为 {$字段名称[行或列编号]},如{$prtnum[1]}、{$prtnum[2]} /// </summary> /// <typeparam name="T"></typeparam> public abstract class Report<T>: IReport where T : class, new() { #region 委托定义 public delegate IList<T> DataFormatDelegate(IList<T> list); public delegate string TemplateFormatDelegate(string template); #endregion #region 字段 private string _currentTable; private string _backupTable; #endregion #region 属性 public int MaxCol; public string DBName { get; set; } public string TableName { get; set; } public string PrtNum { get; set; } public string PhNum { get; set; } public string SyNum { get; set; } public string WtNum { get; set; } #endregion #region 私有函数 private void InitTableName() { _currentTable = TableName.Replace("_bak", "_ggggg").Replace("_grou", "_ggggg").Replace("_ggggg", "_grou"); _backupTable = _currentTable.Replace("_grou", "_bak"); } /// <summary> /// 取得模板内容 /// </summary> /// <returns></returns> private string GetTemplate() { var filepath = string.Format("{0}\\{1}.html", Blackice.Config.WebConfig.ReportTemplate,_currentTable); var strTemplate = FileHelper.ReadTextFile(filepath); return strTemplate; } /// <summary> /// 生成默认查询语句 /// </summary> /// <returns></returns> private string GetQueryString() { var sql = string.Format( "(select * from {0} where prtNum='{1}' ) union all (select * from {2} where prtNum='{1}')", _backupTable, PrtNum, _currentTable); return sql; } #endregion #region 委托函数 /// <summary> /// 带委托的报告获取 /// </summary> /// <param name="dataFormat">数据处理函数(替换前处理)</param> /// <param name="templateFormat">模板处理函数(替换后处理)</param> /// <returns></returns> public string GetReport(DataFormatDelegate dataFormat, TemplateFormatDelegate templateFormat) { InitTableName(); var dataGeter = new ReportDataGeter<T>(DBName); var reportEngine = new ReportTemplateEngine<T>(); //获取模板 var strTemplate = GetTemplate(); //获取数据 var data = dataGeter.GetData(GetQueryString()); for (var i = 0; i < (MaxCol + 2 - data.Count); i++) { data.Add(new T()); } //委托数据处理 data = (dataFormat != null) ? dataFormat(data) : data; //替换数据 strTemplate = reportEngine.ReplaceLabels(strTemplate, data); //委托内容处理 strTemplate = (templateFormat != null) ? templateFormat(strTemplate) : strTemplate; return strTemplate; } #endregion #region 接口的默认实现 /// <summary> /// 获取检验报告 /// </summary> /// <returns></returns> public virtual string GetReport() { return GetReport(null, null); } public string GetDataJson() { return null; } #endregion } }
4、WebUI调用
View Code
using System; using System.Reflection; using TableInterface; namespace Blackice.DWZ.Web.Controls { public class CReport { public static string GetReport(Model.VData obj) { string strTemp; try { var tableName = obj.grou_table.Trim(' '); var dllName = tableName.Replace("_bak", "_grou"); //反射创建类 var assembly = Assembly.LoadFrom(string.Format("{0}\\{1}.dll", Config.WebConfig.BinnPath, dllName)); var typeName = string.Format("{0}.Report", dllName); var t = assembly.GetType(typeName); //判断程序是否继承了IReport接口 if (t.GetInterface("IReport") != null) { var report = (IReport)Activator.CreateInstance(t); report.DBName = obj.DBName; report.TableName = tableName; report.SyNum = obj.sy_num; report.WtNum = obj.wt_num; report.PrtNum = obj.prt_num; report.PhNum = obj.ph_num; strTemp = report.GetReport(); } else { strTemp = string.Format("非法的应用程序:[{0}.dll]。", dllName); } } catch (Exception ex) { strTemp = ex.Message; } return strTemp; } } }
5、扩展一个报表:我们新建一个项目,创建Table.cs数据实体,再新建一个 Report.cs继承抽象类ReportCenter.Report,覆盖原有的GetReport方法,调用委托方法完成数据替换前处理及附加数据替换。
View Code
using System.Collections.Generic; using System.Linq; namespace jggj_grou { public class Report : ReportCreater.Report<Table> { public Report() { MaxCol = 2; } /// <summary> /// 获得报表 /// </summary> /// <returns></returns> public override string GetReport() { return GetReport(FormatList, ReplaceAfter); } /// <summary> /// 二次替换数据 /// </summary> /// <param name="template"></param> /// <returns></returns> private string ReplaceAfter(string template) { var db = new ReportCreater.ReportDataGeter<Table>(DBName); var dt = db.GetDataTable(string.Format("select * from jggj_record where ph_num='{0}'", PhNum)); return (new ReportCreater.DataTableTemplateEngine()).ReplaceLabels(template, dt); } /// <summary> /// 对数据进行一些自定义处理 /// </summary> /// <param name="list"></param> /// <returns></returns> private static IList<Table> FormatList(IList<Table> list) { foreach (var table in list.Where(table => string.IsNullOrEmpty(table.ph_num))) { table.ph_num = "此栏空白"; } return list; } } }
附1:DataTable扩展
View Code
public static class DataTableExtensions { public static List<T> ToList<T>(this DataTable dt) where T : new() { var list = new List<T>(); if (dt == null) return list; var len = dt.Rows.Count; for (var i = 0; i < len; i++) { var info = new T(); foreach (DataColumn dc in dt.Rows[i].Table.Columns) { var value = dt.Rows[i][dc.ColumnName]; if (value == null || string.IsNullOrEmpty(value.ToString())) continue; var p = info.GetType().GetProperty(dc.ColumnName); try { if (p.PropertyType == typeof(string)) { p.SetValue(info, value.ToString(), null); } else if (p.PropertyType == typeof(int)) { p.SetValue(info, int.Parse(value.ToString()), null); } else if (p.PropertyType == typeof(bool)) { p.SetValue(info, bool.Parse(value.ToString()), null); } else if (p.PropertyType == typeof(DateTime)) { p.SetValue(info, DateTime.Parse(value.ToString()), null); } else if (p.PropertyType == typeof(float)) { p.SetValue(info, float.Parse(value.ToString()), null); } else if (p.PropertyType == typeof(double)) { p.SetValue(info, double.Parse(value.ToString()), null); } else { p.SetValue(info, value.ToString(), null); } } catch (Exception) { //p.SetValue(info, ex.Message, null); } } list.Add(info); } dt.Dispose(); dt = null; return list; } /// <summary> /// 按照属性顺序的列名集合 /// </summary> public static IList<string> GetColumnNames(this DataTable dt) { DataColumnCollection dcc = dt.Columns; //由于集合中的元素是确定的,所以可以指定元素的个数,系统就不会分配多余的空间,效率会高点 IList<string> list = new List<string>(dcc.Count); foreach (DataColumn dc in dcc) { list.Add(dc.ColumnName); } return list; } }
附2:通用报表标签替换类
View Code
using System; using System.Collections.Generic; using System.Reflection; namespace ReportCreater { /// <summary> /// 通用报表标签替换类 /// </summary> /// <typeparam name="T"></typeparam> public class ReportTemplateEngine<T> { /// <summary> /// 替换标签 /// </summary> /// <param name="template">模板数据</param> /// <param name="list">IList数据源</param> /// <returns></returns> public string ReplaceLabels(string template, IList<T> list) { if (list == null) { return template; } try { for (var index = 0; index < list.Count; index++) { var obj = list[index]; var infos = obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); var columIndex = index + 1; foreach (var info in infos) { string value = GetFieldValue(obj, info); var field = @"{$" + info.Name + columIndex + @"}"; template = template.Replace(field, value); } } } catch (Exception) { } return template; } /// <summary> /// 获取字段的值 /// </summary> /// <param name="obj"></param> /// <param name="info"></param> /// <returns></returns> private string GetFieldValue(T obj, PropertyInfo info) { string value; try { value = info.GetValue(obj, null).ToString(); } catch (Exception) { value = "---"; } if (string.IsNullOrEmpty(value.Trim(' '))) { value = "---"; } return value; } } }
附3:通用DataTable标签替换类
View Code
using System; using System.Data; namespace ReportCreater { /// <summary> /// 通用DataTable标签替换类 /// </summary> public class DataTableTemplateEngine { /// <summary> /// 替换标签 /// </summary> /// <param name="template">模板内容</param> /// <param name="dt">DataTable数据</param> /// <returns></returns> public string ReplaceLabels(string template, DataTable dt) { if (dt == null) { return template; } try { for (var index = 0; index < dt.Rows.Count; index++) { var obj = dt.Rows[index]; var infos = dt.GetColumnNames(); var columIndex = index + 1; foreach (var info in infos) { var value = GetFieldValue(info, obj); var field = @"{$" + info + "[" + columIndex + "]" + @"}"; template = template.Replace(field, value); } } } catch (Exception) { } return template; } /// <summary> /// 获取字段的值 /// </summary> /// <param name="info"></param> /// <param name="obj"></param> /// <returns></returns> private string GetFieldValue(string info, DataRow obj) { string value; try { value = obj[info].ToString(); } catch (Exception) { value = "---"; } if (string.IsNullOrEmpty(value.Trim(' '))) { value = "---"; } return value; } } }
附4:报告数据读取类
View Code
using System; using System.Collections.Generic; using System.Data; using System.Reflection; using SqlHelper; namespace ReportCreater { /// <summary> /// 报告数据读取类 /// </summary> public class ReportDataGeter<T> where T : new() { private IDatabase _conn { get; set; } private readonly string _dbName; public ReportDataGeter(string dbname) { _dbName = dbname; _conn = GetDatabase(); } /// <summary> /// 获取数据库连接 /// </summary> /// <returns></returns> private IDatabase GetDatabase() { //根据指定的dbname反射创建数据访问接口 var assembly = Assembly.LoadFrom(string.Format("{0}\\SqlHelper.dll", Blackice.Config.WebConfig.BinnPath)); var typeName = string.Format("SqlHelper.{0}DB", _dbName); var t = assembly.GetType(typeName); var db = (IDatabase)Activator.CreateInstance(t); return db; } /// <summary> /// 执行SQL语句获取数据 /// </summary> /// <param name="sql"></param> /// <returns></returns> public DataTable GetDataTable(string sql) { return _conn.Execute(sql); } /// <summary> /// 执行SQL语句获取数据,并转换为LIST /// </summary> /// <param name="sql">指定的SQL命令</param> /// <returns></returns> public IList<T> GetData(string sql) { var dt = GetDataTable(sql); IList<T> list = dt.ToList<T>(); return list; } } }