ASP.Net Expression Builders. Часть 1Источник: andir-notesblogspot
В первой части этой статьи я хочу описать малоизвестный и практически неиспользуемый трюк в ASP.Net. Примечание: Это первая часть статьи про Expressions Builders. Обратите внимание, что есть и вторая часть. Речь пойдёт о механизме расширения возможностей инлайн-разметки в виде декларативных выражений <%$ ExpressionPrefix: ExpressionKey %> . Возможность использования таких выражений предоставляется механизмом под названием Expression Builders. <asp:Literal runat="server" Text='<%$ ExpressionPrefix: ExpressionKey %>' /> Каждое выражение состоит из префикса ExpressionPrefix и ключевого элемента ExpressionKey . По префиксу определяется соответствующий Expression Builder , который отвечает за вычисление значения выражения по ключу ExpressionKey. Суть работы Expression Builders: выражение парсится и по префиксу ищется ранее зарегистрированный билдер, который затем генерирует код для получения значения этого выражения на этапе выполнения. Стандартные билдерыПо умолчанию, в ASP.Net зарегистрировано три набора ExpressionBuilder.
Как, собственно, можно понять из их префиксов, они занимаются генерацией кода для подстановки значений из Web.config и глобальных ресурсов приложения. Примечание: Увидеть, воочию, их регистрацию можно в файле [%SystemRoot%\Microsoft.NET\Framework\v2.0.50727\CONFIG\Web.config] и далее ищем секции [configuration/system.web/compilation/expressionBuilders] Протестируем их работу: web.config: <?xml version="1.0"?> <configuration> <appSettings> <add key="MyAppSetting" value="My AppSetting value from web.config"/> </appSettings> <connectionStrings> <add name="MyConnectionString" connectionString="Server=.\SqlExpress;Database=MyDatabase;Trusting_Connection=False"/> </connectionStrings> </configuration> App_GlobalResources\MyResources.resx <?xml version="1.0" encoding="utf-8"?> <root> <!-- Removed unnecessary details --> <data name="MyResourceKey" xml:space="preserve"> <value>Some string from resource</value> </data> </root> default.aspx: <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Home.Andir.Examples.DefaultPage" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>ASP.Net Expression Builder Test</title> </head> <body> <h1>ASP.Net Expression Builder Test</h1> <table class=borderall cellpadding="3" cellspacing="0"> <col style="color: DarkGreen" align="left" /> <tr> <th>$AppSettings</th> <td><asp:Label runat="server" Text='<%$ AppSettings: MyAppSetting %>' /></td> </tr> <tr> <th>$ConnectionStrings</th> <td><asp:Label runat="server" Text='<%$ ConnectionStrings: MyConnectionString %>' /></td> </tr> <tr> <th>$Resources</th> <td><asp:Label runat="server" Text='<%$ Resources: MyResources, MyResourceKey %>' /></td> </tr> </table> </body> </html> Запускаем и смотрим результат: Как видим, все значения из файла конфигурации и глобальных ресурсов вывелись на экран. Реализуем свой билдерКак уже было упомянуто, механизм генерации кода "Expression Builders" является расширяемым и можно реализовать собственное выражение. Для этого нужно создать наследник абстрактного класса ExpressionBuilder и реализовать метод GetCodeExpression. Этот метод возвращает свой результат в виде CodeDom Expression. Реализуем примитивный билдер, который переворачивает зеркально свой аргумент. ReverseExpressionBuilder.cs using System.CodeDom; using System.Linq; using System.Web.Compilation; using System.Web.UI; namespace Home.Andir.Examples { public class ReverseExpressionBuilder : ExpressionBuilder { public override CodeExpression GetCodeExpression( BoundPropertyEntry entry, object parsedData, ExpressionBuilderContext context) { string key = entry.Expression; return new CodePrimitiveExpression( ReverseString(key) ); } private static string ReverseString(string key) { return new string(key.ToCharArray().Reverse().ToArray()); } } } После того, как реализовали свой ExpressionBuilder необходимо зарегистрировать новое выражение и связать с ним префикс. Делается это в файле конфигурации. Web.config <?xml version="1.0"?> <configuration> <system.web> <compilation debug="true"> <expressionBuilders> <add expressionPrefix="ReverseExpression" type="Home.Andir.Examples.ReverseExpressionBuilder" /> </expressionBuilders> </compilation> </system.web> </configuration> После регистрации префикса, выражение можно использовать в разметке ASP.Net. TestExpression.aspx <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="TestExpression.aspx.cs" Inherits="Home.Andir.Examples.TestExpressionPage" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>ASP.Net Custom Expression Builder Test</title> </head> <body> <h1>ASP.Net Custom Expression Builder Test</h1> <table class=borderall cellpadding="3" cellspacing="0"> <col style="color: DarkGreen" align="left" /> <tr> <th>$ReverseExpression</th> <td><asp:Label runat="server" Text='<%$ ReverseExpression: Каждый охотник желает знать, где сидит фазан! %>' /></td> </tr> </table> </body> </html> Результат: Как видно на скриншоте, наше выражение успешно отработало. Осталось только посмотреть как это работает изнутри и для этого заглянем в результат компиляции страницы TestExpression.aspx (несущественные детали выкинуты). Примечание: Результаты компиляции страниц ASP.Net всегда можно найти в папке [%SystemRoot%\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files]. //------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. // Runtime Version:2.0.50727.3053 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // </auto-generated> //------------------------------------------------------------------------------ namespace ASP { [System.Runtime.CompilerServices.CompilerGlobalScopeAttribute()] public class testexpression_aspx : global::Home.Andir.Examples.TestExpressionPage, System.Web.SessionState.IRequiresSessionState, System.Web.IHttpHandler { // details skipped ... [System.Diagnostics.DebuggerNonUserCodeAttribute()] private global::System.Web.UI.WebControls.Label @__BuildControl__control4() { global::System.Web.UI.WebControls.Label @__ctrl; @__ctrl = new global::System.Web.UI.WebControls.Label(); @__ctrl.ApplyStyleSheetSkin(this); @__ctrl.Text = System.Convert.ToString("!назаф тидис едг ,ьтанз теалеж кинтохо йыджаК", System.Globalization.CultureInfo.CurrentCulture); return @__ctrl; } [System.Diagnostics.DebuggerNonUserCodeAttribute()] public override void ProcessRequest(System.Web.HttpContext context) { base.ProcessRequest(context); } } } То есть в результате парсинга страницы, в сгенерированный код автоматически был встроен результат выполнения ReverseExpressionBuilder.GetCodeExpression(…). |