定义ExpressionBuilder减少服务器端的代码耦合度
在System.Web.Compilation下默认提供了三个常用的ExpressionBuilder,比如在UI上快速访问AppSettings可以这么写:
1 <%= ConfigurationManager.AppSettings["IsDebug"] %> 2 <%$ appSettings:IsDebug %>
%$ appSettings:IsDebug %>实际上调用了AppSettingsExpressionBuilder,这一层的转换到调用由ASP.NET页面的动态编译器负责(其实到真正动态执行某个页面时所加载的类型并不是我们编写代码时的那个类型,而是被动态编译后的派生类型)。再比如我们可以使用ConnectionStringsExpressionBuilder快速访问数据库连接字符(在ASP.NET的默认Web.config下分别注册了appSettings、connectionStrings和resources):
<asp:SqlDataSource ID="sqlDataSource1" runat="server" ConnectionString="<%$ connectionStrings:mainDB%>" />
可以看出使用这类ExpressionBuilder的目的是帮助我们将那些完全和业务逻辑无关的代码放置在ASPX页面中,比如使用TextBox显示当前时间:
<asp:TextBox ID="tbDtNow" runat="server" Text="<%=DateTime.Now%>" />
执行结果:
<input name="tbDtNow" type="text" value="<%=DateTime.Now%>" id="tbDtNow" />
上面输出的并不是我们所想要的结果,当然完全可以使用非服务器端控件来达到相同的目的:
<input type="text" id="tbDtNow" value="<%=DateTime.Now%>" />
许多情况下我们必须使用服务器端控件,而这些服务器端控件的某些和UI相关的资源需要我们动态构造,比如ImageButton、TreeView。它们通常需要指定图片(ImageUrl、ExpandImageUrl…)。
<asp:ImageButton ID="btnSearch" runat="server" ImageUrl="searchWebHS.png" OnClick="searchWebHS_Click" />
this.btnSearch.ImageUrl = ResolveClientUrl(String.Format("~/Skins/{0}/searchWebHS.png", this.SkinID));
ASPX:
ImageUrl="Skins/<%=SkinID%>/searchWebHS.png" ImageUrl=’<%="Skins/" + SkinID + "/searchWebHS.png"%>’
以上代码都无法被动态解析正确。如果使用服务器端代码方式对于以后和UI相关的修改就需要每次编译。所以相对较好的解决方式就是创建一个自定义的ExpressionBuilder。比如上面关于动态构造ImageUrl可以直接返回表达式并动态计算执行。下面演示如何编写一个自定义的ExpressionBuilder(关于ExpressionBuilder的更详细介绍建议查阅MSDN):
1 [ExpressionPrefix("code")] 2 public class RuntimeCodeExpressionBuilder : ExpressionBuilder 3 { 4 public RuntimeCodeExpressionBuilder() { } 5 6 public override CodeExpression GetCodeExpression(BoundPropertyEntry entry, Object parsedData, ExpressionBuilderContext context) 7 { 8 return new CodeSnippetExpression(entry.Expression); 9 } 10 11 public override Boolean SupportsEvaluate 12 { 13 get { return true; } 14 } 15 }
GetCodeExpression返回需要动态计算的代码表达式,而CodeSnippetExpression表示原意代码段(关于CodeExpression和CodeObject可以查阅和CodeDom相关的文档)。RuntimeCodeExpressionBuilder的类Attribute定义了使用的前缀表达式,如果想要自定义Design时的编辑器可以声明ExpressionEditor。重新修改ImageButton的ImageUrl:
<asp:ImageButton ID="btnSearch" runat="server" ImageUrl=’<%$ code:ResolveClientUrl("~/Skins/") + SkinID + "/searchWebHS.png"%>’ OnClick="searchWebHS_Click" />
同时需要在Web.config中注册新的ExpressionBuilder:
system.web ->compilation -> expressionBuilders <expressionBuilders> <add expressionPrefix="code" type="ExpressionBuilderDemo1.RuntimeCodeExpressionBuilder,ExpressionBuilderDemo1" /> </expressionBuilders>