在項目中經常遇到一個問題,打印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();
}
這樣就可以對表達進行精確的解析了,當然目前還有一些未考慮完全的地方 ,待各位看客老爺指點。
好的~今天就貼到這裡, 後期看看被噴的程度來確定是否繼續在博客園發一些日志