首先来看看Asp.Net中的表达式构造器是如何实现的
默认在C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\CONFIG\web.config中我们可以找到如下配置节:
<
expressionBuilders
>
< add expressionPrefix ="Resources" type ="System.Web.Compilation.ResourceExpressionBuilder" />
< add expressionPrefix ="ConnectionStrings" type ="System.Web.Compilation.ConnectionStringsExpressionBuilder" />
< add expressionPrefix ="AppSettings" type ="System.Web.Compilation.AppSettingsExpressionBuilder" />
</ expressionBuilders >
在这里定义了三个表达式构造器:Resources,ConnectionStrings,AppSettings。因此我们可以在页面中直接使用它们,比如可以使用<%$AppSettings:aa %>来读取AppSettings的配置。注意这里使用的是
$符号,它是读取表达式构造器的专用标识。
< add expressionPrefix ="Resources" type ="System.Web.Compilation.ResourceExpressionBuilder" />
< add expressionPrefix ="ConnectionStrings" type ="System.Web.Compilation.ConnectionStringsExpressionBuilder" />
< add expressionPrefix ="AppSettings" type ="System.Web.Compilation.AppSettingsExpressionBuilder" />
</ expressionBuilders >
接下来重点看看如何实现自己的表达式构造器
我们的目的是实现一个简单的Xml表达式构造器,可以读取指定xml文件中的配置信息,并且在页面设计阶段就可以看到效果。
一、修改配置
在自己的web.config中加入配置:
<
expressionBuilders
>
< add expressionPrefix ="Xml" type ="MyResource.XmlExpressionBuilder, MyResource, Version=1.0.0.0, Culture=neutral, PublicKeyToken=94a835118357b2d3" />
</ expressionBuilders >
表示我们的表达式构造器的前缀为Xml,也就是在页面中使用<%$Xml:.... %>的方式来读取
< add expressionPrefix ="Xml" type ="MyResource.XmlExpressionBuilder, MyResource, Version=1.0.0.0, Culture=neutral, PublicKeyToken=94a835118357b2d3" />
</ expressionBuilders >
我们自定义的表达式构造器类的类名为XmlExpressionBuilder, 特别注意这个类所在的程序集需要使用强签名
二、实现表达式构造器类(ExpressionBuilder)
首先我们的类需要从ExpressionBuilder继承
public
class
XmlExpressionBuilder : ExpressionBuilder
实现GetCodeExpression方法,这个方法是在页面实际运行时计算表达式的值使用的。它是用来为页面初始化生成代码(在允许页面编译时才会调用到此方法,Asp.Net在默认情况下是允许页面编译的):
public
override
CodeExpression GetCodeExpression(BoundPropertyEntry entry,
object
parsedData, ExpressionBuilderContext context)
{
if ((entry.DeclaringType == null ) || (entry.PropertyInfo == null ))
{
return new CodeMethodInvokeExpression( new CodeTypeReferenceExpression( base .GetType()), " GetXmlKey " , new CodeExpression[] { new CodePrimitiveExpression(entry.Expression.Trim()) });
}
return new CodeMethodInvokeExpression( new CodeTypeReferenceExpression( base .GetType()), " GetXmlKey " , new CodeExpression[] { new CodePrimitiveExpression(entry.Expression.Trim()), new CodeTypeOfExpression(entry.DeclaringType), new CodePrimitiveExpression(entry.PropertyInfo.Name) });
}
这个方法主要就是动态的调用GetXmlKey这个自定义的方法:
{
if ((entry.DeclaringType == null ) || (entry.PropertyInfo == null ))
{
return new CodeMethodInvokeExpression( new CodeTypeReferenceExpression( base .GetType()), " GetXmlKey " , new CodeExpression[] { new CodePrimitiveExpression(entry.Expression.Trim()) });
}
return new CodeMethodInvokeExpression( new CodeTypeReferenceExpression( base .GetType()), " GetXmlKey " , new CodeExpression[] { new CodePrimitiveExpression(entry.Expression.Trim()), new CodeTypeOfExpression(entry.DeclaringType), new CodePrimitiveExpression(entry.PropertyInfo.Name) });
}
![ContractedBlock.gif](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
![ExpandedBlockStart.gif](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
//取得Xml中的key值,为了测试,没有考虑性能和异常的问题
public static string GetXmlKey(string strKey)
{
string[] keys = strKey.Split(',');
string strFile = HttpContext.Current.Server.MapPath("/") + keys[0] + ".xml";
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(strFile);
XmlNodeList nodeList = xmlDoc.SelectSingleNode("test").ChildNodes;
foreach (XmlNode xn in nodeList)
{
if (xn is XmlElement)
{
if (xn.Name == keys[1])
{
return (xn as XmlElement).GetAttribute("value");
}
}
}
return "";
}
public static object GetXmlKey(string key, Type targetType, string propertyName)
{
return GetXmlKey(key);
}
实现EvaluateExpression和SupportsEvaluate方法,这两个方法是在禁用页面编译时才会调用的,比如在页面中设置如下:
<%
@ Page Language
=
"
C#
"
CodeBehind
=
"
Default.aspx.cs
"
Inherits
=
"
MyResource._Default
"
CompilationMode="Never"
%>
这种情况下就会调用这两个方法来取得表达式的值:
//
返回一个值,该值指示是否可在不编译的页中计算表达式
public override bool SupportsEvaluate
{
get
{
return true ;
}
}
// 返回当前表达式的计算结果(禁用页面编译时 ---CompilationMode="Never" )
public override object EvaluateExpression( object target, BoundPropertyEntry entry, object parsedData, ExpressionBuilderContext context)
{
return GetXmlKey(entry.Expression, target.GetType(), entry.PropertyInfo.Name);
}
public override bool SupportsEvaluate
{
get
{
return true ;
}
}
// 返回当前表达式的计算结果(禁用页面编译时 ---CompilationMode="Never" )
public override object EvaluateExpression( object target, BoundPropertyEntry entry, object parsedData, ExpressionBuilderContext context)
{
return GetXmlKey(entry.Expression, target.GetType(), entry.PropertyInfo.Name);
}
![ContractedBlock.gif](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
![ExpandedBlockStart.gif](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
<?xml version="1.0" encoding="GB2312"?>
<test>
<test1 value="测试1"/>
<test2 value="测试2" />
</test>
最后在自己的Aspx页面中调用:
<
asp:Label ID
=
"
Label1
"
runat
=
"
server
"
Text
=
"
<%$Xml:test,test1 %>
"
></
asp:Label
>
< br />
< asp:Label ID = " Label3 " runat = " server " Text = " <%$Xml:test,test2 %> " ></ asp:Label >
< br />
运行此页面就可以正确的显示test.xml中对应的值了。不过现在还有一个问题就是在页面的设计界面不能正确的显示test.xml中的值,因此我们还要接下来实现表达式编辑器类。
< br />
< asp:Label ID = " Label3 " runat = " server " Text = " <%$Xml:test,test2 %> " ></ asp:Label >
< br />
三、实现表达式编辑器类(ExpressionEditor)
首先在XmlExpressionBuilder上加入类属性,指定使用哪个表达式编辑器类
[ExpressionEditor(
"
MyResource.XmlExpressionEditor, MyResource, Version=1.0.0.0, Culture=neutral, PublicKeyToken=94a835118357b2d3
"
), ExpressionPrefix(
"
Xml
"
)]
public class XmlExpressionBuilder : ExpressionBuilder
public class XmlExpressionBuilder : ExpressionBuilder
接下来实现自己的表达式编辑器类:XmlExpressionEditor,它必须从ExpressionEditor继承
public
class
XmlExpressionEditor : ExpressionEditor
在我们的例子中只需要实现EvaluateExpression这个方法就可以了,它就是用来在页面的设计阶段来取得表达式的值的
public
override
object
EvaluateExpression(
string
expression,
object
parseTimeData, Type propertyType, IServiceProvider serviceProvider)
{
if (serviceProvider != null )
{
IWebApplication service = (IWebApplication)serviceProvider.GetService( typeof (IWebApplication));
if (service != null )
{
System.Configuration.Configuration configuration = service.OpenWebConfiguration( true );
if (configuration != null )
{
string strFile = configuration.FilePath.Substring( 0 , configuration.FilePath.LastIndexOf( " \\ " ));
string [] keys = expression.Split( ' , ' );
strFile = strFile + " \\ " + keys[ 0 ] + " .xml " ;
return XmlExpressionBuilder.GetXmlKey(expression, strFile);
}
}
}
return "" ;
}
在这里也是通过调用XmlExpressionBuilder类中的GetXmlKey方法的,但是由于在设计状态下是取不到HttpContext.Current的值的,因此在这个方法中我通过IServiceProvider接口来取得当前路径,将得到xml文件名作为参数传递给GetXmlKey方法。修改后的GetXmlKey方法如下:
{
if (serviceProvider != null )
{
IWebApplication service = (IWebApplication)serviceProvider.GetService( typeof (IWebApplication));
if (service != null )
{
System.Configuration.Configuration configuration = service.OpenWebConfiguration( true );
if (configuration != null )
{
string strFile = configuration.FilePath.Substring( 0 , configuration.FilePath.LastIndexOf( " \\ " ));
string [] keys = expression.Split( ' , ' );
strFile = strFile + " \\ " + keys[ 0 ] + " .xml " ;
return XmlExpressionBuilder.GetXmlKey(expression, strFile);
}
}
}
return "" ;
}
![ContractedBlock.gif](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
![ExpandedBlockStart.gif](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
//取得Xml中的key值,为了测试,没有考虑性能和异常的问题
public static string GetXmlKey(string strKey, string strFileName)
{
string[] keys = strKey.Split(',');
string strFile = "";
if (String.IsNullOrEmpty(strFileName))
{
strFile = HttpContext.Current.Server.MapPath("/") + keys[0] + ".xml";
}
else
{
strFile = strFileName;
}
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(strFile);
XmlNodeList nodeList = xmlDoc.SelectSingleNode("test").ChildNodes;
foreach (XmlNode xn in nodeList)
{
if (xn is XmlElement)
{
if (xn.Name == keys[1])
{
return (xn as XmlElement).GetAttribute("value");
}
}
}
return "";
}
public static object GetXmlKey(string key, Type targetType, string propertyName)
{
return GetXmlKey(key, "");
}
public static string GetXmlKey(string strKey)
{
return GetXmlKey(strKey, "");
}
这样就可以在页面的设计视图取得正确的值了(当修改了XmlExpressionEditor文件后,重新编译后可能在设计视图还是不能正确显示,需要把VS2005重新启动一下就可以了)