在项目中经常遇到一个问题,打印word或者打印excel的时候,我们经常使用一对一的赋值或者批量替换的方式来对模板进行修改。
但是现在遇到两种场景:
1、取值是通过自定以方法进行取值的。
如:一个销售单据,会涉及到很多种费用,并且这些费用是由后台配置的,非常灵活。但是我们在制作打印模板时取值各项费用我们该如何去定义他呢,如何给他赋值呢?我们如果针对这一个场景下的模板进行一个特殊定义后,在打印另一份单据或者遇到同样的取值非常灵活的数据,是不是也需要进行特殊处理呢。
2、取值是通过自行定义进行取值的。
如:还是一个销售单据,我们打印的可能是销售价格,成本、毛利,但是如果我们打印的时候涉及到提成配比,提成配比可能是根据销售价格算的,可能根据毛利算的,可能根据效益来算的,那么是不是我们在做这个模板的时候定义:提成(按成本)、提成(按毛利)、提成...。
在这中情况下,我的解决方案是采用反射与javascript进行处理:
这里大致讲述一下我的解决思路,各位过路大神,各位奋战一线的程序猿们,看过笑过,不喜勿喷~
第一步:建立两种Eval方法,来解析表达式
C#Eval反射式:(此种方式主要应对在程序中自定义的方法,根据参数及方法来模拟程序中的计算,并将结果返回过去,这种方法必须制定处理他的主体Object)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
/// <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等),灵活方便
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
/**/
/// <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代码,并且对代码断进行计算
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
|
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();
}
|
这样就可以对表达进行精确的解析了,当然目前还有一些未考虑完全的地方 ,待各位看客老爷指点。