The CodeExpressionBuilder

A very exciting new feature in ASP.NET 2.0 is Expression Builders. Expression builders allow for some pretty interesting interaction with the ASP.NET compilation model.

For example, new to ASP.NET 2.0 is the ability to reference appSettings declaratively. Lets say you wanted the text of a button to be based on a value in the appSettings section of your web.config. Piece of cake:

<asp:Button id="cmdSubmit" runat="server" Text="<%$ appSettings: ButtonText %>" />

This is made possible by the built in AppSettingsExpressionBuilder. Lets say you wanted to display a localizable string, which is stored as a resource. In ASP.NET 1.1 it was sort of a pain. No longer:

<%$ resources: ResourceKey %>

That's the ResourceExpressionBuilder hard at work. Nice!

There's also a ConnectionStringExpressionBuilder so you can refer to Connection Strings defined in the new connection strings section in the web.config. That is extremely helpful when working with the new declarative data controls:

<asp:SqlDataSource id="data1" runat="server"
    ConnectionString="<%$ ConnectionStrings: MyConnectionString %>"/>

Pretty useful.

Now lets take it one step further. Have you ever seen this exception?


The dreaded "Server tags cannot contain constructs" exception.

That's because you probably tried to do something like this:

<asp:Label id="lbl1" runat="server" Text=<%= CurrentUserName %> />

Perhaps you tried to fix it by putting it in quotes:

<asp:Label id="Label1" runat="server" Text="<%= CurrentUserName %>" />

... Only to be thwarted once again, as the literal text, including the <%= %> construct, ended up in the page:


Putting <%= %> in quotes doesn't help much.

If all you want to do is show the result of this code as a string, you could quite simply just get rid of the label:

<%= CurrentUserName %>

That would work. But perhaps you need the label server control for some other reason, or perhaps you need to set a string property of some other type of server control in this way.

The problem is you have incorrectly (although intuitively) tried to assign the property of a server control using the <%= %> construct. Unfortunately that is simply not supported by ASP.NET, 1.1 nor 2.0. If you ask around about your problem, someone may tell you that you will have to convert to using the <%# %> databinding construct instead. That is advice that I have given myself. But it requires that you are calling DataBind() on the control, AND, it will cause you to BLOAT VIEWSTATE... and you KNOW how much I hate bloating viewstate!

You can just go ahead and assign the value in your code-behind, say.. in the OnInit method.. but that too will bloat viewstate. I'm afraid there's no super simple solution unless you don't mind disabling viewstate on that control. That may work in some scenarios, but sometimes you really need the ViewState enabled! What's a web developer to do?

Let me back up a little and give a more concrete example. You want the text value of a CheckBox to be the current date and time. For whatever reason, you can't disable viewstate on the CheckBox (say, because you need the CheckChanged event, which doesn't work without viewstate). How on Earth are you going to get the current date and time into the Text property on the CheckBox, WITHOUT BLOATING VIEWSTATE? By "Bloating ViewState", I mean causing data to become serialized in the __VIEWSTATE hidden form field when it isn't necessary to begin with. There's no reason to put the current Date and Time into serialized viewstate, is there? It's going to be reassigned on the next request. You'd just be making ASP.NET serialize it, then making the user's browser pull the serialized string down the pipe, then making them push it back up the pipe to the server on a postback, then making ASP.NET deserialize the value -- ONLY FOR IT TO BE REASSIGNED? How rude! How wasteful and inefficient. For a single control its not a big deal, what's a few bytes? But that is not a path you want to start down my friend... with that kind of mantra, your web forms will quickly grow a viewstate tumor the size of a Borg cube! Ideally, you want the functional equivalence of this:

<asp:CheckBox id="chk1" runat="server" Text="<%= DateTime.Now %>"/>

That is ideal, because the ViewState StateBag is not tracking changes when ASP.NET assigns declared attributes, so our beloved ViewState remains optimized. And, we didn't even have to disable ViewState. AND we can do it declaratively! Woohoo! Right. Well, it won't work.

The CodeExpressionBuilder comes to the rescue! Another great thing about ExpressionBuilders is that you can roll your own. So, I created a CodeExpressionBuilder, one that allows you to use raw code to assign values to control properties. Using the CodeExpressionBuilder you can do this:

<asp:CheckBox id="chk1" runat="server" Text="<%$ Code: DateTime.Now %>"/>

Now that is nice. And to think the darn thing is only a few lines of code! The trick is to use the CodeDom's CodeSnippetExpression to convert the given string into a CodeExpression. Here's the entire class:

[ExpressionPrefix("Code")]
public class CodeExpressionBuilder : ExpressionBuilder {
    public override CodeExpression GetCodeExpression(BoundPropertyEntry entry, 
       object parsedData, ExpressionBuilderContext context) {
        return new CodeSnippetExpression(entry.Expression);
    }
}

To use it, or any custom ExpressionBuilder for that matter, you must register it in the web.config expressionBuilders section. Now... how you do this part sort of depends on how your project is setup. If you have a standard ASP.NET Web Site project, then you will be defining the CodeExpressionBuilder class in the app_code directory, and the "type" will just be "CodeExpressionBuilder". However, if you are creating a Web Application Project (read about it here), then the CodeExpressionBuilder is just another class in your project, with its own namespace. For that you will need to define the whole type string (or, if you define it in a reusable library, you'll need the fully qualified type and assembly name). In my case that is "Infinity.Web.Compilation.CodeExpressionBuilder". Like this:

<compilation debug="true">
  <expressionBuilders>
    <add expressionPrefix="Code" type="Infinity.Web.Compilation.CodeExpressionBuilder"/>
  </expressionBuilders>
</compilation>

And to see it in action:

<asp:CheckBox id="chk1" runat="server" Text="<%$ Code: DateTime.Now %>" />

ExpressionBuilders are truly a thing of beauty! Use any expression you want!

Also try:

<%$ Code: DateTime.Now.AddDays(1) %>
<%$ Code: "Hello World, " + CurrentUserName %>
<%$ Code: CurrentUserName.ToUpper() %>
<%$ Code: "Page compiled as: " + this.GetType().AssemblyQualifiedName %>

Just be careful what combination of quotes you use. If you have literal strings in your code expression like in two of the above examples, you will need to use single quotes (') to surround the entire <%$ %> expression if it is within a server control declaration. If you don't you will get the "Server tag is not well formed" error.

In the beginning I said ExpressionBuilders were an interesting way to plug into the ASP.NET compilation model. Well this really illustrates that... put a break point in the expression builder, and debug the page. You will hit the break point once, and only once, even after you refresh the page several times. The Date and Time will continue to update, but the expression builder breakpoint will only activate the first time you hit the page. The reason is because the ExpressionBuilder is used when ASP.NET compiles the page. Once the page is compiled, that's it. That's the reason why ExpressionBuilder returns a CodeExpression, and not an actual object. In essence, the builder tells ASP.NET what code it needs to run to get the value, instead of giving it the actual value. It's the old adage, teach a man to fish, and he eats forever. How geeky is that? Too cool.

PS: The one thing this ExpressionBuilder doesn't do... it won't work in "No-Compile" pages. Seems like a reasonable limitation.

Questions? Comments? Random odd facts?

HAPPY (dynamic) CODING!

UPDATE 09/05/2006: Thanks to the tip from Kelly, made the code even shorter. Also fixed a typo in the web.config snippet. The "Type" attribute should be lower case, "type".

83 Comments

  • Pretty cool indeed. Keep the good posts coming, man!

  • Couldn't get an expression builder registered from an external assembly.

  • What issue were you having Mr. Anon? If you send me what you've got maybe I can help.

  • You are truly a badass.
    Thanks for the articles, they are great (this one and the one about the viewstate).
    Also thanks for the Darkside theme. I will try it out.

  • Is there any reason why you couldn&#39;t drop &#39;ParseExpression&#39; and use &#39;entry.Expression&#39; in &#39;GetCodeExpression&#39; instead?

  • Kelly -- you're absolutely right, ParseExpression is not necessary. I updated the code in the article. Now it really is only a few lines long. Thanks :)

  • So, what about using it for something other than Text output like setting a property on the control? &nbsp;For example:
    &lt;asp:CheckBox id=&quot;chk1&quot; runat=&quot;server&quot; Text=&quot;button&quot; Visible=&#39;&lt;%$ Code: Datetime.Now.Day==1 %&gt;&#39; /&gt;

  • Yeah, no problem. You can set any property with it, as long as the expression returns the correct data type. There's nothing special about the Text property on a checkbox, just happened to be the example I used.

  • Hi,
    I&#39;m just doing a migration from a Web Site projet to a Web Application projet following the same link you provide in your article.
    I use an ExpressionBuilder and the tag I use in the web.config is quite the same as yours, i.e. something like that:
    &lt;expressionBuilders&gt;
    &lt;add expressionPrefix=&quot;Code&quot; type=&quot;MyNameSpace.MyExpressionBuilderClass&quot;/&gt;
    &lt;/expressionBuilders&gt;
    When I launch th emigration on the ascx control that uses this expression builder I got the foloowing error:
    Could not load type &#39;MyNameSpace.MyExpressionBuilderClass&#39; from assembly &#39;System.Web&#39;
    It tries to load my expression builder from the assembly System.Web !!
    And if I add the name of my own assembly in the fully qualified name of my expression builder type, it doesn&#39;t work either...indeed, my web application doesn&#39;t compile since this ascx control has not been converted...
    Any Idea about this issue?
    Thanks,
    David

  • Gareth -- the namespace is actually System.Web.Compilation, not Configuration. Try that.

    You also need System.CodeDom.

    As for the arrow keys... you mean while typing in the comment box, you can't go up/down? It works for me... what browser are you using?

  • After create the IdExpressionBuilder like following code, i found the ClientID just equals to ID, even the control is in a user control, do you know why? (my english is limited, sorry)
    // IdExpressionBuilder.cs in APP_Code
    using System;
    using System.CodeDom;
    using System.Web.UI;
    using System.Web.Compilation;
    using System.Web;
    namespace Monster.Web
    {
    &nbsp; &nbsp;[ExpressionPrefix(&quot;clientID&quot;)]
    &nbsp; &nbsp;public class IdExpressionBuilder : ExpressionBuilder
    &nbsp; &nbsp;{
    &nbsp; &nbsp; &nbsp; &nbsp;public override CodeExpression GetCodeExpression(BoundPropertyEntry entry,
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;object parsedData, ExpressionBuilderContext context)
    &nbsp; &nbsp; &nbsp; &nbsp;{
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;return new CodeSnippetExpression(entry.Expression + &quot;.ClientID&quot;);
    &nbsp; &nbsp; &nbsp; &nbsp;}
    &nbsp; &nbsp;}
    }
    //WebUserControl1.ascx
    &lt;%@ Control Language=&quot;C#&quot; ClassName=&quot;WebUserControl&quot; Debug=&quot;true&quot; %&gt;
    &lt;asp:Button ID=&quot;Button1&quot; runat=&quot;server&quot; Text=&quot;Button&quot; /&gt;
    &lt;asp:TextBox ID=&quot;TextBox1&quot; runat=&quot;server&quot; Text=&quot;&lt;%$ clientID: Button1 %&gt;&quot;&gt;&lt;/asp:TextBox&gt;

  • The unique ID and client ID of a control isn't established until its part of the control tree. Expressions are evaluated before they are added to the control tree, so its too soon. You could just use the databinding syntax instead, , and then be sure to call DataBind.

  • Nice article.
    Can you clarify when in the event life cycle the expressions are evaluated. &nbsp;I set a variable during Page_Load. However the expression evaluates to what the variable was initialized to in the Dim declaration. &nbsp;I should mention I&#39;m trying to pass a dynamic value to a custom control like
    &lt;sd:DateFieldWithCalendar name=&quot;startdate&quot; value=&quot;&lt;%$ Code: StartDate %&gt;&quot; runat=&quot;server&quot; /&gt;

  • Phil, the expressions are evaluated at the same time the other markup attributes are evaluated, which is during the creation of all the controls on the page (very early: before OnInit).

    In other words, this is only meant for dynamic data is immediately available or calculable. So if you can call say GetStartDate() and have that method return the correct value even if its before the page OnInit method, great.

    If not, then your data is best suited for DataBinding. Use and then call DataBind on that control when you want it to execute, such is in OnLoad on !IsPostBack.

  • I'm new to ASP so I've probably missed something but when I run my app, i get the following " Compilation Error..."
    Line 14: ///
    Line 15: [ExpressionPrefix("Code")]
    Line 16: public class CodeExpressionBuilder : ExpressionBuilder {
    Line 17: public override CodeExpression GetCodeExpression(BoundPropertyEntry entry,
    Line 18: object parsedData, ExpressionBuilderContext context) {


  • Dan -- you didn't paste the compilation error itself. You are probably seeing squigly lines under all the types it doesn't recongize. You need to make sure you are 'using' all the namespaces that are needed.

    to the top of the code file add:

    using System.Web.Compilation;
    using System.CodeDom;

    in addition to whatever using statements you already have.

  • Yeah, I figured this out a few months ago as well.

    Thing is, I hate having bits of code strewn about the page. It really makes maintenance harder.

    This was one of the reasons I stopped using ColdFusion. There's code just littered everywhere in a typical CF page, and when you're looking at some total train wreck of a page that someone wrote two years ago, it becomes really, really hard to figure out what's going on.

  • Awesome, you just saved me a lot of hassle. Thanks!

  • It is great, it help me to solve most of my problem.

    It works on the text on buttons and textbox,
    however, it is not work on the OnClientClick event

    <asp:Button ID="showDate" runat="server" Text="Show Date" UseSubmitBehavior="false" OnClientClick="alert('');" >

    Can it also be solved?

  • Herb, you have to understand that the expression applies to an entire property value, you can't intermix multiple expressions.

    so you can do it by including everything in the expression itself:

    (assuming C# here)

    OnClientClick=''

    It may look confusing with all those quotes. The expression itself is a string (javascript), which contains quotes (the parameter to alert), which contains dynamic data (the server date) outside of the quotes.

  • Marvin - 'currentusername' was just an example I was using, it isnt a built in function. You'll have to create the logic for getting the current user's name yourself, which can depend on what authentication mechanism you are using.

  • It works great, if I type it in the html view. In the properties window however, this expression does not show up. If I go to the expressions window after typing the expression in html view, It alerts me like "TextBox1 Expressions \n The Expression prefix 'Code' was not found in the config. \n You will need to add an definition to your config file for the page to function properly." thanks.

  • Could something explain the '<%$' syntax?

  • hi. great post by the way ... i'm trying to make it work in a button control like:

    <asp:button skinid="btnSkin" style="background:url('') repeat-x;" runat="server" />

    i also tryied:
    <asp:button skinid="btnSkin" style="background:url('') repeat-x;" runat="server" />, but then i get a not well formed tag

    any ideas how to fix it?

  • What is up with these nested comments ... ugh.

    Ken -- the expression has to apply to the entire property value, not just a part of it.

    wrong:
    foo="abcxyz"

    right:
    foo=''

  • Can this be used to set a sqldatasource connectionstring
    dynamically?

    Thanks!

  • Yes -- any property can be set with it. Just keep in mind that the property will be set very very very early in the request -- before OnInit even. So don't expect the value to be able to depend on anything dynamic, unless its something you can determine prior to OnInit.

  • w00t! This is exactly what I needed. Thanks!

  • Thanks for the great post! The CodeExpressionBuilder has been very useful in the project I'm working on.

    I use it mostly for dynamically displaying controls based on a user's role (e.g. - I set the visible property to ''). I know that this works, but I'm not exactly sure why. As specified in previous posts, the expression gets evaluated very early on in the lifecycle. My question is that I'm not exactly sure when the Page's properties (in this case "User") get set. Must be early on since the code works, but I was hoping you could give a more precise explanation?

    Thanks again!

  • I get next error:
    Literal expressions like '&lt;%$ Code: Localization.GetString("btnCopy",LocalResourceFile)%&gt;' are not allowed. Use &lt;asp:Literal runat="server" Text="&lt;%$ Code: Localization.GetString("btnCopy",LocalResourceFile)%&gt;" /&gt; instead.
    I try to load a control dynamically with this code in a linkbutton:
    &nbsp; &nbsp; &nbsp; &nbsp;&lt;asp:LinkButton ID="btnCopy" runat="server" CausesValidation="true"
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;OnClick="btnCopy_Click"&gt; &nbsp;&lt;img src='&lt;%$ Code: ImageBtnCopy%&gt;' &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; alt='&lt;%$ Code: Localization.GetString("btnCopy",LocalResourceFile)%&gt;' &nbsp;/&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
    &nbsp; &nbsp; &nbsp; &nbsp;&lt;/asp:LinkButton&gt;

  • Jelle -- you should do what the error message suggests. It's saying you can't have free-floating expressions like that, they need to be expressed as properties of server controls. Using a literal control to do that would do the same thing you intended but with a server control.

  • Any ideas why I get this? (I have validated that the assembly is in the assemblies section)

    Parser Error Message: Could not load type 'iTmove.Web.Compilation.CodeExpressionBuilder'.

    Source Error:


    Line 35:
    Line 36:
    Line 37:
    Line 38:
    Line 39:

  • Javier -- verify the type name is correct. Verify the assembly is in the bin or gac. Verify that the assembly can be loaded in the app, e.g. that it not missing any dependencies. Try adding the assembly name in the type attribute for good measure ("typename, assemblyname[, strongname]"), although being in the assemblies section I think should prevent the need for it, but worth a shot.

  • You mentioned that using the data binding syntax () would bloat ViewState.

    If you call DataBind inside OnInit before ViewState is tracking, will that still bloat ViewState? If so, can you explain why?

  • fm --

    From within OnInit, the control or page's OWN viewstate is not yet tracking, but child controls ARE. That's because OnInit occurs in reverse order -- by the time your OnInit occurs, your child controls have already gone through OnInit and have begun tracking.

    The only way to to programmatically set the property of a child control before it is tracking viewstate without using a derived control specifically for that purpose is to do it from page PreInit, handling the Init event of the control and doing it from the handler, or catch it when its added to the page by overriding AddedControl in its parent.

  • Hey there, ignore my last question (but not the compliments and thanks!) Solved my own problem, had a litteral inside the quotes of the tag, just moved it to within the expression. Knew it wasn't allowed as you stated above, just didn't see it. So went from ErrorMessage='*' to ErrorMessage=''

    It's late, thankfully I figured it out so now I can sleep...

    Cheers,
    Phill

  • Hi, thanks for to code and the amazing article TRULY Understanding ViewState.
    I have something like this:
    <asp:SiteMapPath ID="MapaBasico" runat="server" SiteMapProvider="">

    and this:

    if (Session[0].ToString() == "")
    Response.Redirect("~/Inicio.aspx");

    When the session is null I redirect to Inicio.aspx, but asp evaluates this first. So when session is null I get an error. ¿How can I avoid that?

    <asp:SiteMapPath ID="MapaBasico" runat="server" SiteMapProvider=""


    Thanks.

  • try this:



    You shouldn't need the ToString call in there, ASP.NET takes care of converting expressions to the property type.

  • Oh and use single quotes (') instead of double quotes to contain that expression, since it contains double quotes. Or you'll get an error about the server tag not being well formed.

  • hey thanks, but I have to do this instead.


    Anyway, I learn how to use conditions with your answer,
    ¿What else can I do?

    Again, thanks and excelent work, I'm reading all your articles and they are great.

  • Darragh -- when I say at the same time as other attributes, I don't mean code blocks. Those run during RENDER, very late in the life cycle. Expressions specified with , whether they are this code expression builder or any other, are executed when the control tree is first created, which is one of the very first things that happens in the life cycle. If you need it to execute later, you'll have to use some other means, like databinding with .

  • Ok, thanks for your speedy reply. I understand better now. I just with I could get these server control propreties to evalute during render, same as other code blocks.

  • You say above that the ConnectionString property is no different from any other, and that was my theory, too. I've created my own expression builder for use in the ConnectionString property of a SqlDataSource declarative object. It runs fine, but Visual Studio doesn't understand it. I get a blue squiggly in the codebehind with the error: &quot;ProductMembersDataSource is not a member of my_page.aspx.&quot;

    I wonder if you've ever seen this, and if so, do you know what I might be doing wrong? Thanks!

    It's declared in web.config:

    &lt;expressionBuilders&gt;
    &lt;add expressionPrefix="MyExp" type="MyExpressionBuilder"/&gt;
    &lt;/expressionBuilders&gt;


    I use it in an .aspx page:

    &lt;asp:SqlDataSource ID="ProductMembersDataSource" runat="server" SelectCommand="mgr_getProdMembers" ConnectionString="&lt;%$ MyExp: ProductConnectionString %&gt;" ProviderName="System.Data.SqlClient" SelectCommandType="StoredProcedure" CacheDuration="10"&gt;
    &lt;SelectParameters&gt;
    &lt;asp:ControlParameter ControlID="Hidden_PID" Name="pid" PropertyName="Value"
    Type="String" /&gt;
    &lt;/SelectParameters&gt;
    &lt;/asp:SqlDataSource&gt;


    And the class is defined in app_code/MyExpressionBuilder.vb:


    Public Class MyExpressionBuilder
    Inherits ExpressionBuilder

    Public Overrides Function GetCodeExpression(ByVal entry As System.Web.UI.BoundPropertyEntry, ByVal parsedData As Object, ByVal context As System.Web.Compilation.ExpressionBuilderContext) As System.CodeDom.CodeExpression
    Dim param As String = parsedData(0) 'entry.Expression
    Dim args As String() = CType(parsedData, String())
    HttpContext.Current.Trace.Write("GetCodeExpression", String.Format("Length of parsed args is {0}", args.Length))

    Dim x As New MyData.ConnectionDictionary
    Dim ret As New CodePrimitiveExpression()
    HttpContext.Current.Trace.Write("GetCodeExpression", String.Format("Fetching expression for {0}", param))
    Select Case param
    Case "ContextConnectionString"
    ret.Value = Me.ConnectionLibrary.Context
    Case "NavigationConnectionString"
    ret.Value = Me.ConnectionLibrary.Navigation
    Case "OrderConnectionString"
    ret.Value = Me.ConnectionLibrary.Order
    Case "ProductConnectionString"
    ret.Value = Me.ConnectionLibrary.Product
    Case "ShopperConnectionString"
    ret.Value = Me.ConnectionLibrary.Shopper
    Case "ConnectionString"
    ret.Value = Me.ConnectionLibrary.Product

    Case Else
    ret.Value = HttpContext.Current.Application(param)

    End Select
    Return ret
    End Function

    Public Overrides Function ParseExpression( _
    ByVal expression As String, _
    ByVal propertyType As Type, _
    ByVal context As ExpressionBuilderContext _
    ) As Object
    Dim l_exp As String() = expression.Split(New Char() {"(", ",", ")"})
    For Each x As String In l_exp
    HttpContext.Current.Trace.Write("Parsing Expression", x)
    Next
    Return l_exp
    End Function



    Private Function ConnectionLibrary() As MyData.ConnectionDictionary
    Return System.Web.HttpContext.Current.Application.StaticObjects("ConnLib")
    End Function

    End Class

  • Turns out it was a security thing because I was working on a UNC share. I had to use caspol.exe to grant the share full trust. Now the code expression is recognized in VS2005 as well as when I run the site.

    Thanks!

  • Love the idea. Works great by itself. However, I am trying to concatenate to the expression builder another string:

    I have my appSettings:


    now in my control I am trying to use it in a style :

    Style="background-image: url(Images/bg.jpg);"

    However, I noticed that you cannot concatenate the Expression with the Images/bg.jpg.
    Is there a way around ?

  • Seb -- not with the AppSettings expression builder. If you used the CodeExpressionBuilder you could do it:

    Style='<%$ Code: "background-image: url(" + GetSetting() + "Images/bg.jpg)"'

    GetSetting is just pseudo code.

  • how to assign the filename in linkbutton in asp.net using vb.net

  • I want to place a button on my master page, and store it's PostBackUrl in each of the slave pages.

    Does the expression builder support a page-level prefix, rather than 'application' prefix? How would I store the variable in the slave page?

    Alternatively, I could put the NextUrl property in a HiddenField in my slave page. But how do I reference a hidden field in the asp:button?

    Is there any other way?

    I'm currently achieving this with a page-load event on the slave pages, but i'd prefer to do it with some kind of markup or simple variable.

    thanks

  • johny -- sorry, I don't know what you mean by "store" the url on the slave pages. If you need to access the url from the 'slave' page you can put a property on the master page, or the slave page can find the control and get it directly.

  • Johny -- Page_Load does not cause overhead. You are mistaking it for some kind of client-to-server operation. It's not, its just something that fires while the page is going through its normal life cycle. That method fires before the server is done processing the request. I think you think it means when the page is loaded on the client, but thats not the case. There is no server-side event for that. In fact, even Page_*UN*Load occurs before there's even a single html element displaying in the browser window.

    Declaring a variable like you show isn't going to help at all. You have to understand that pages and master pages are compiled into their own classes. Declaring a variable isnt the same as it is in classic ASP or other procedural server side technology, its not a global page level variable at all.

    Your Page_Load solution is fine. But I would change it so that code is within an override to Render, so it doesn't bloat your viewstate. That or just disable ViewState on the button.

  • ok, fair enough, thanks. but, i'd love to know:

    --how can an asp:button can get a code-behind variable in it's PostBackUrl?

    --how can i declare a custom page property, as you mentioned? in the page tag?

    --how can an asp:button reference a page property in it's PostBackUrl?

    --can an aspx page contain cofig tags, like the web.config file?

    --can i declare an expression on an aspx page? how would an asp:button reference it?

    MANY THANKS

  • Excellent Code , your are a King of Development

  • Hi,

    I have tried your code.It runs properly.

    Now I want the data from the database.
    Suppose I am having the datalist control in which I am having some controls like imagebutton,labels ,textboxes,etc.Now I want to show the data on my page from database by binding the datalist from database.

    Can You please tell me how do I use the expression builders from database.


    Thanks
    It's urgent

  • The code expression is not special, it's just code. How you access your database depends on your project, the database, if you have an object model, etc etc. Using an expression like this for database access isn't what I recommend, for that you should probably be using databinding and the data source controls. Search for SqlDataSource or ObjectDataSource for plenty of literature on how to use them.

  • public override CodeExpression GetCodeExpression(BoundPropertyEntry entry, object parsedData, ExpressionBuilderContext context)
    {


    DbProvider getContent = new DbProvider();
    subContent = getContent.getcontenttext();
    //while (subContent.Read())
    return new CodeSnippetExpression(entry.Expression);
    }

    public SqlDataReader getcontenttext()
    {
    SqlParameter[] sqlParam = new SqlParameter[1];
    SqlDataReader subContent;

    //sqlParam[0] = new SqlParameter("@contentid", SqlDbType.Int);
    //sqlParam[0].Value = entry;
    //sqlParam[0].Size = 4;


    initializeDBConnection();
    subContent = execSelectByKey("Select.Content");
    return subContent;
    }

    and following is my query
    select contenttext from ContentManagement where contentid=1

    what exactly return new CodeSnippetExpression(entry.Expression); statement returns and what value is displayed by entry.Expression.


    Now this "1" I am passing to in the .
    I am returning this query as sqldatareader.
    Now I want to show the data where it is returning the entry.Expression i.e 1.
    if I write
    while (dr.read)
    {
    return new CodeSnippetExpression(entry.Expression);

    }
    it gives me an error : no all code returns a value.
    So, how should I display and write the query where the id is 1 in the databse which is I am passing to .

    Please help me
    It' s urgent

  • sweety -- you are barking up the wrong tree completely. Expression builders are NOT for database access. I suggest you follow my previous advice and read up on using the SqlDataSource control and the databound controls in ASP.NET.

  • Thank you. Very usefull article

  • Do you have a VB.NET example. I've been trying to translate your example without success. Also, is not valid in my web.config file. Should the class below be in a specific folder?

    Thx!

    _
    Public Class CodeExpressionBuilder
    Inherits System.Web.Compilation.ExpressionBuilder

    Public Overrides Function ParseExpression(ByVal expression As String, ByVal propertyType As Type, ByVal context As System.Web.Compilation.ExpressionBuilderContext) As Object
    Return expression
    End Function 'ParseExpression

    Public Overrides Function GetCodeExpression(ByVal entry As BoundPropertyEntry, ByVal parsedData As Object, ByVal context As System.Web.Compilation.ExpressionBuilderContext) As CodeExpression

    Return New CodeSnippetExpression(parsedData.ToString())
    End Function 'GetCodeExpression
    End Class 'CodeExpressionBuilder

  • I've run into a situation that seems very related to this article, but ultimately I can't find a solution. I'm using ASP.NET 2.0 w/ c#.

    I have 2 repeaters. A top repeater and a bottom one. The bottom one contains buttons and textboxes. In my app, if one of those buttons is clicked or the textbox is changed, content in both the top and bottom repeater is supposed to change. The button fires the OnCommand event and the textbox fires the TextChanged event. I've noticed the following behavior:

    1) If you use <%= to dynamically display information in the top repeater, the content in the top repeater updates.
    2) If you use <%# to dynamically display information in the top repeater, the content in the top repeater does NOT update! This is a killer for my app! I'm about to to move this to the Page_Load event.

    To be more specific (and maybe this is the bug), I'm actually calling a method in my code behind (I have to) in both #1 and #2. I'd love to use ExpressionBuilder, but I need some data from the Container.DataItem object (the local variable created only for <%# syntax).

    To me, this is a shortcoming of .NET and I've been trying to research it and haven't heard anyone else getting this issue. This is the closest article I've found.

  • figured it out...

    needed:
    Imports System.CodeDom
    Imports System.Web

    _
    Public Class CodeExpressionBuilder
    Inherits Compilation.ExpressionBuilder
    Public Overloads Overrides Function GetCodeExpression(ByVal entry As BoundPropertyEntry, ByVal parsedData As Object, ByVal context As Compilation.ExpressionBuilderContext) As CodeExpression
    Return New CodeSnippetExpression(entry.Expression)
    End Function
    End Class


    in web.config:






    Thanks for the cool code!

  • Man! you Really Rock!
    Thanks a lot! for some other good advice on, dynamic coding in ASP .NET 2
    Cheers

  • This is beyond cool. However, heed the comments and be nice and post a VB version in future. That way those of us who speak English instead of gobbledigook can understand it.

    Here is my VB version:

    _
    public class code_expression
    inherits expressionbuilder

    public overrides function getcodeexpression
    (entry as boundpropertyentry, _
    parseddata as object, _
    context as expressionbuildercontext) as codeexpression
    return new codesnippetexpression (entry.expression)
    end function 'getcodeexpression

    end class 'code_expression

    And in action (I needed to use an enum in my tags):

    <custom tab_button button_id='' />

    rts

  • perfect whole day spent but the true way is found. Thank you very much.

  • Great post. Used it for custom connectionString in SqlDataSource!

  • More than 2 years later and this post is still helpful. Thanks for making my life just a bit easier!

  • This post's really helpful, thanks for it, you just solved one of my problems, in an elegant way :)

  • Great article in usefulness and insight into .NET features.

    However first rule of .net articles is include namespaces in .net articles :).

    For completeness you will need at minimum these 3 for the sample code:

    using System.Web.Compilation;
    using System.Web.UI;
    using System.CodeDom;

  • Thank you... I spent a couple of hours on this before I found your page... you're a lifesaver.

  • Would you expect multiple calls to the same code snippet to return the same results on a single page load?
    Say for a simple example you called new guid in a for loop:
    <asp:Literal runat="server" Text="" />

    This returns the same guid multiple times (for a single page load). Each new page load gives a new (duplicated) guid.

  • I was actually trying to use the code expr builder to dump the contents of a list on an asp .net page into a server control and cant figure out how i could do that.
    For example I would like all the entries in the array 'aa' to be sent to the server control:


    <asp:Literal runat="server" Text="" />



    Any ideas on how to do this?

  • omfg... I can't believe I haven't found this blog post before! I've always abused DataBinding with some AutoBind workaround for this. Finally, I can unbind my views from my codebehind where necessary!

    Tnx a lot (after more than 2 years)

  • Thank you, Great code!

  • Hi - your code works great! Thanks a lot.

    Just one question though. If I try and use it in a user control of my own, for example:


    <myControl:showSomeTest ID="idShowText" text="" runat="server" />

    All that control does is take an id and gets text that corresponds to the id and returns it.

    But when that is rendered, it display:


    Any way to get the ExpressionBuilder to work the same for User Controls??

    Thanks

  • And a BIG thank you that is. Your great post solved a big issue for me.. now it is so easy to make ASP.NET controls visible or invisible declaratively, depending on the logged user attributes which are stored in a session object. Again , sincere thanks.

  • Is there a way to get back the expression on the control's property in code behind. If yes, what the method would require as parameters?
    Please help.

  • Is there a way to retrieve the expression back in code behind?

  • gary -- no. The expression is translated into code injected into a dynamic class, then forgotten. Why would you need that ability?

  • I am providing ability to users to change the text of static label's on screen by means of my custom control. All text choices are coming from database.
    In order to complete the functionality, I have to update specific record in database. This can be done only if I am aware of the expression set originally in code behind.

    I was prefering to avoid burden of additional property to store the same info.
    But thanks for your reply.
    I was pushing myself hard to have some means of conversion to BoundPropertyEntry object. No luck yet :)
    After reading your reply, I would probably pick my other option.

    Thanks again.

  • There are some articles that help you realize the power of ASP.NET.
    This is one example.
    Thank you.....for such a Nice one...:)

  • @MikeB: Expressions have to take the entire attribute value. So rather than this:

    foo="abcxyz"

    You do this:

    foo=''

    (note the use of single quotes around the whole value, so as to not confuse the parser since the value contains double quotes)

  • Worth mentioning is that the code will be evaluated just after creating control, so using getter of page property, that will be set e.g. in PageLoad event, will be unsuccessful.
    But one can "inject" some more code by the ExpressionBuilder, to force setting the property one more time during chosen page event, e.g. during PreRender:

    string expr = string.Format("new Func(() => {{this.PreRender += (_s, _e) => @__ctrl.{0} = {1}; return {1};}})()", entry.Name, entry.Expression);
    return new CodeSnippetExpression(expr);

  • For those looking for a VB.NET version (as I was):

    Public Class CodeExpressionBuilder
    Inherits ExpressionBuilder

    Public Overrides Function GetCodeExpression(ByVal entry As System.Web.UI.BoundPropertyEntry, ByVal parsedData As Object, ByVal context As System.Web.Compilation.ExpressionBuilderContext) As System.CodeDom.CodeExpression
    Return New CodeDom.CodeSnippetExpression(entry.Expression)
    End Function

    End Class

  • Hello.

    Thanks for the useful post.
    Is there any easy way to add "using statements" to the environment in which the expression is evaluated? Only fully qualified names seem to work for me.

Comments have been disabled for this content.