Using custom expression builders

Hi all,

For my first blogpost I would like to give some attention to a very nice - but not so widely known - feature in Asp.Net 2.0 called "Custom Expression Builders". There is a new expression type in 2.0 that looks like this: "<%$ %>". U can use it to load values from your AppSettings, but it gets more fun when you start using it to dynamically load resources in statements like this:

<asp:Literal runat="server" Text="<%$ CustomResource, SomeValue %>" />

It allows you to dynamically set values on your controls. There's loads of fun stuff you can do with that. I have thankfully adopted this mechanism to avoid the use of FindControl. You often run into a situation where you will have to perform a FindControl in the Onload event of your webform, to apply some settings to a control on your page. Although FindControl usually gets the job done, I've never been a big fan of it. You create a depency on the Control's ID and things start falling apart when switching to masterpages and nested control inside your placeholders. And then there's code readabilty: you are changing the behaviour or attributes of some control in your masterpage or page, where it would be much nicer to let the control figure it out for itself.

An example of how you can use Custom Expression Builders:
I have been working on a website that allows the user to navigate back to an overview page from a more detailed page. I use a sitemap for all my navigation, and I want to retrieve the Url from my SiteMap Provider. There are loads of ways to do this, an obvious one would be to write a custom control and setting the values in the custom control's life cyle events. I don't like writing custom controls for everything, so I gave it a go with Custom Expressions.

In my masterpage I have the following hyperlink control (using the .Net 2.0 navigation controls is a but much for a single link):
<asp:HyperLink runat="server" TabIndex="7" rel="nofollow" ID="overviewlink"
 ToolTip="<%$ Resources: Control, ReturnLinkTooltip  %>"
 NavigateUrl="<%$ DynamicUrl: PreviousNavigationLevel %>"
 Text="<%$ Resources: Control, ReturnLinkText  %>" />

Now, the custom expression type is used in the NavigateUrl="<%$ DynamicUrl: ShrinkFont %>" statements. The tooltip and text just get their values from a regular resource file to ensure there is no content hard coded in my masterpage and to globalise the link. Then I create a custom expression builder class, like so:

    /// <summary>
    /// Provide Dynamic Urls for Fixed Page Hyperlinks
    /// </summary>
    public class UrlExpression : System.Web.Compilation.ExpressionBuilder
    {
        #region Private properties
        private static UriBuilder m_Pageuri;
        #endregion

        #region Public static properties

        /// <summary>
        /// Returns the pageUri
        /// </summary>
        public static UriBuilder Pageuri
        {
            get {
                    try
                    {
                        Uri url = HttpContext.Current.Request.Url;
                        m_Pageuri = new UriBuilder(url.Scheme, url.Host, url.Port, url.AbsolutePath);
                    }
                    catch (ArgumentOutOfRangeException ex)
                    {
                        System.Diagnostics.Trace.WriteLine(string.Format("Error constructing the uri: {0}", ex.Message));
                    }
                    catch (Exception ex)
                    {
                        System.Diagnostics.Trace.WriteLine(string.Format("Error formatting the url: {0}", ex.Message));
                    }
                return m_Pageuri;
            }
        }
        #endregion

        #region Overriden System.Web.Compilation.ExpressionBuilder members
        /// The GetCodeExpression returns the expression to be evaluated when the Page parser finds a declarative expression and calls the required ExpressionBuilder.
        public override CodeExpression GetCodeExpression(BoundPropertyEntry entry, object parsedData, ExpressionBuilderContext context)
        {
            CodeTypeReferenceExpression thisType = new CodeTypeReferenceExpression(base.GetType());
            CodePrimitiveExpression expression = new CodePrimitiveExpression(entry.Expression.Trim().ToString());
            string evaluationMethod = "GetUrlValue";
            return new CodeMethodInvokeExpression(thisType, evaluationMethod, new CodeExpression[] { expression });
        }
        #endregion

        #region Public methods
        // Retrieves the requested url value
        public static string GetUrlValue(string expression)
        {
            string returnvalue = string.Empty;
            UriBuilder returnUri = new UriBuilder(Pageuri.Uri);

            switch (expression)
            {
                case "PreviousNavigationLevel":
                    string sitemapPath = string.Empty;
                    try
                    {
                        // get the default sitemap provider
                        SiteMapProvider Provider = SiteMap.Provider;
                        if (Provider != null)
                        {
                            // My custom sitemapprovider always sets the currentnode to the same sitemap level
                            SiteMapNode Current = Provider.CurrentNode;
                            if (Current != null)
                            {
                                sitemapPath = Current.Url;
                            }
                        }
                    }
                    catch (NullReferenceException e)
                    {
                        // health monitoring
                        sitemapPath = string.Empty;
                    }
                    if (!string.IsNullOrEmpty(sitemapPath))
                    {
                        returnUri.Path = sitemapPath;
                    }
 
                    break;
                 default:
                    // Do Nothing
                    break;
            }

     returnvalue = returnUri.Uri.PathAndQuery;
            if (!string.IsNullOrEmpty(returnvalue))
            {
                return returnvalue;
            }
            return string.Empty;
        }
        #endregion
    }

Based on the expression requested, it returns the appropriate url.

There's one last thing you have to do, because your application doesn't know about this custom resource you created. In the <compilation /> section of your web.config add the following configuration:
      <expressionBuilders>
        <add expressionPrefix="DynamicUrl" type="Rinze.Utility.UrlExpression"/>
      </expressionBuilders

By calling the DynamicUrl resource from my webpages and adding case statements to my UrlExpression class, I can now easily expand and provide all dynamic url's without using anythign other than hyperlinks.

Hope this helps!

Rinze

 

Published Saturday, January 26, 2008 11:02 AM by rinze

Comments

# Using custom expression builders | videositemap.com

Saturday, January 26, 2008 10:56 AM by Using custom expression builders | videositemap.com

Pingback from  Using custom expression builders | videositemap.com

# re: Using custom expression builders

Sunday, January 27, 2008 5:45 AM by Mohamed Meligy

My all times most favorite use of expression builders: ;-)

weblogs.asp.net/.../The-CodeExpressionBuilder.aspx

Leave a Comment

(required) 
(required) 
(optional)
(required)