在項目中經常遇到一個問題,打印word或者打印excel的時候,我們經常使用一對一的賦值或者批量替換的方式來對模板進行修改。
但是現在遇到兩種場景:
1、取值是通過自定以方法進行取值的。
如:一個銷售單據,會涉及到很多種費用,並且這些費用是由後台配置的,非常靈活。但是我們在制作打印模板時取值各項費用我們該如何去定義他呢,如何給他賦值呢?我們如果針對這一個場景下的模板進行一個特殊定義後,在打印另一份單據或者遇到同樣的取值非常靈活的數據,是不是也需要進行特殊處理呢。
2、取值是通過自行定義進行取值的。
如:還是一個銷售單據,我們打印的可能是銷售價格,成本、毛利,但是如果我們打印的時候涉及到提成配比,提成配比可能是根據銷售價格算的,可能根據毛利算的,可能根據效益來算的,那麼是不是我們在做這個模板的時候定義:提成(按成本)、提成(按毛利)、提成...。
在這中情況下,我的解決方案是采用反射與javascript進行處理:
這裡大致講述一下我的解決思路,各位過路大神,各位奮戰一線的程序猿們,看過笑過,不喜勿噴~
第一步:建立兩種Eval方法,來解析表達式
C#Eval反射式:(此種方式主要應對在程序中自定義的方法,根據參數及方法來模擬程序中的計算,並將結果返回過去,這種方法必須制定處理他的主體Object)
/// <summary> /// CShrapEval 的摘要說明 /// </summary> public class CShrapEval { /// <summary> /// 計算結果,如果表達式出錯則拋出異常 /// </summary> public static object Eval(string action,Type type,object obj,object[] parm) { return type.InvokeMember( action, BindingFlags.InvokeMethod, null, obj, parm ); } public static object Eval(string Cstring, Type type, object obj) { string action = Cstring.Split('|')[0]; object[] parm = Cstring.Split('|')[1].Split(','); return type.InvokeMember( action, BindingFlags.InvokeMethod, null, obj, parm ); } }
JavaScript腳本編譯方式:模擬javascript工作方式去處理一個表示式,可以使用一個javascript常用函數(如getdate() length等),靈活方便
/**/ /// <summary> /// 動態求值 /// </summary> public class JavaEval { /**/ /// <summary> /// 計算結果,如果表達式出錯則拋出異常 /// </summary> /// <param name="statement">表達式,如"1+2+3+4"</param> /// <returns>結果</returns> public static object Eval(string statement) { return _evaluatorType.InvokeMember( "Eval", BindingFlags.InvokeMethod, null, _evaluator, new object[] { statement } ); } /**/ /// <summary> /// /// </summary> static JavaEval() { //構造JScript的編譯驅動代碼 CodeDomProvider provider = CodeDomProvider.CreateProvider("JScript"); CompilerParameters parameters; parameters = new CompilerParameters(); parameters.GenerateInMemory = true; CompilerResults results; results = provider.CompileAssemblyFromSource(parameters, _jscriptSource); Assembly assembly = results.CompiledAssembly; _evaluatorType = assembly.GetType("Evaluator"); _evaluator = Activator.CreateInstance(_evaluatorType); } private static object _evaluator = null; private static Type _evaluatorType = null; /**/ /// <summary> /// JScript代碼 /// </summary> private static readonly string _jscriptSource = @"class Evaluator { public function Eval(expr : String) : String { return eval(expr); } }"; }
第二步、構建好兩個eval之後我們就需要在程序中去識別那些是C#,那些是javascript代碼斷
這裡我處理的辦法是:<c ...代碼 /> 和<J ...代碼 />使用這兩種方式分別標示是那種代碼
然後在處理中我們只需要找出那些是C代碼 那些是J代碼,並且對代碼斷進行計算
public void ExportDoc() { ExportReplace(); foreach (NodeTemplate temp in DocTemplateList) { ExportDoc(temp); } if (ActionObject != null) { //動態取值 ExportDymic(); } } //定義C表達式 System.Text.RegularExpressions.Regex RegexC = new System.Text.RegularExpressions.Regex(@"\<C\w*\|\w*[\,\w*]*\\\>"); //定義J表達式 System.Text.RegularExpressions.Regex RegexJ = new System.Text.RegularExpressions.Regex(@"\<J^\>*\\\>"); //業務邏輯理論為先處理C在處理J,但是C與J由存在循環處理的過程 public void ExportDymic() { var MatchesS = RegexC.Matches(doc.GetText()); foreach (System.Text.RegularExpressions.Match MatchC in MatchesS) { string Cstring = MatchC.Value.Replace("<C", "").Replace("\\>", ""); string result = CEval(Cstring); //CShrapEval.Eval(Cstring, this.GetType(), this).ToString(); //A = A.Replace(MatchC.Value, result); doc.Range.Replace(MatchC.Value, result, false, false); } MatchesS = RegexJ.Matches(doc.GetText()); foreach (System.Text.RegularExpressions.Match MatchC in MatchesS) { string Jstring = MatchC.Value.Replace("<J", "").Replace("\\>", ""); string result = JavaEval.Eval(Jstring).ToString(); doc.Range.Replace(MatchC.Value, result, false, false); } } public string CEval(string A) { var MatchesS = RegexC.Matches(A); foreach (System.Text.RegularExpressions.Match MatchC in MatchesS) { string Cstring = MatchC.Value.Replace("<C", "").Replace("\\>", ""); string result = CEval(Cstring).ToString(); A = A.Replace(MatchC.Value, result); } MatchesS = RegexJ.Matches(A); foreach (System.Text.RegularExpressions.Match MatchC in MatchesS) { string Jstring = MatchC.Value.Replace("<J", "").Replace("\\>", ""); string result = JEval(Jstring).ToString(); A = A.Replace(MatchC.Value, result); } return CShrapEval.Eval(A, ActionObject.GetType(), ActionObject).ToString(); } public string JEval(string A) { var MatchesS = RegexC.Matches(A); foreach (System.Text.RegularExpressions.Match MatchC in MatchesS) { string Cstring = MatchC.Value.Replace("<C", "").Replace("\\>", ""); string result = CEval(Cstring).ToString(); A = A.Replace(MatchC.Value, result); } MatchesS = RegexJ.Matches(A); foreach (System.Text.RegularExpressions.Match MatchC in MatchesS) { string Jstring = MatchC.Value.Replace("<J", "").Replace("\\>", ""); string result = JEval(Jstring).ToString(); A = A.Replace(MatchC.Value, result); } return JavaEval.Eval(A).ToString(); }
這樣就可以對表達進行精確的解析了,當然目前還有一些未考慮完全的地方 ,待各位看客老爺指點。
好的~今天就貼到這裡, 後期看看被噴的程度來確定是否繼續在博客園發一些日志