Diego Vega

Entity Framework news and sporadic epiphanies

February, 2009

Posts
  • Diego Vega

    EntityDataSource and Bind: What are those square brackets?

    • 9 Comments

    This post is about a small issue that I have seen in the forums and that arises often in cases in which EntityDataSource is used in combination with bound controls that use templates, like FormsView or a GridView with template based columns.

    If you can create your page correctly, simply using the different drag & drop features and context menus provided by the design surface. However, in the end, some Bind expressions will get enclosed in square brackets that produce compile or run-time errors, then your code may look like this:

    <EditItemTemplate>
        <asp:DropDownList ID="CategoryDropDownList" runat="server"
            DataSourceID="CategoryDataSource"
            DataTextField="CategoryName"
            DataValueField="CategoryID"
            SelectedValue='<%# Bind("[Category.CategoryID]") %>'>
        </asp:DropDownList>
    </EditItemTemplate>

    This annoyance may keep you blocked until you realize how simple the workaround is: just remove the square brackets!

    People that watched my video here, may have noticed that I had to remove the square brackets manually around minute 8:10. I wish I had blogged about it then, but it wasn’t clear at the time I recorded that video that the issue would still be there in the RTM version.

    Here is a short explanation:

    In order to adapt entity objects that do not have foreign key properties to work better with ASP.NET databinding, we decided to wrap each entity returned with a databinding wrapper object that, among other things, flattens the members of EntityKeys in related entities (for more details on how and why the EntityDataSource returns wrapped entities you can go here).

    The name of flattened reference keys are usually composed by the corresponding navigation property, plus the name of the property in the related end. For instance, in a Product, the key of the associated Category, will be exposed as “Category.CategoryID”. We decided to use dots to delimit the parts of the name because that plays well and is consistent with the use of dots in most programming languages, and also in Eval expressions, like the one used in the following code:

    <ItemTemplate>
        <asp:Label ID="CategoryLabel" runat="server" 
            Text='<%# Eval("Category.CategoryName") %>'>
        </asp:Label>
    </ItemTemplate>

    The problem is that for the particular case of Bind (often used in template based databound controls to perform two-way databinding) the design time component of ASP.NET will catch property names that contain dots, and it will try to escape them by surrounding them with square brackets (leading to ta Bind expression of the form “[Categories.CategoryID]”).

    Unfortunately, the rest of the ASP.NET components which need to evaluate the binding expression are actually not capable of parsing the square brackets escaping notation (i.e. for DataBinder.Eval() the brackets actually indicate that a lookup in an indexed property is necessary, and there is no indexed property in this case).

    Given that the EntityDataSource in fact exposes property descriptors that contain dots in their names, the escaping is unnecessary. So, next time you find this issue, just remove the square brackets.

    The good news is that the issue will be fixed in the next release.

    Hope this helps!

  • Diego Vega

    Server queries and identity resolution

    • 0 Comments

    I answered a Connect issue today that deals with a very common expectation for users of systems like Entity Framework and LINQ to SQL. The issue was something like this:

    When I run a query, I expect entities that I have added to the context and that are still not saved but match the predicate of the query to show up in the results.

    Reality is that Entity Framework queries are always server queries: all queries, LINQ or Entity SQL based, are translated to the database server’s native query language and then evaluated exclusively on the server.

    Note: LINQ to SQL actually relaxes this principle in two ways:

    1. Identity-based queries are resolved against the local identity map. For instance, the following query shall not hit the data store:

    var c = context.Customers
        .Where(c => c.CustomerID == "ALFKI");

    2. The outermost projection of the query is evaluated on the client. For instance, the following query will create a server query that projects CustomerID and will invoke a client-side WriteLineAndReturn method as code iterates through results:

    var q = context.Customers
        .Select(c => WriteLineAndReturn(c.CustomerID));
    But this does not affect the behavior explained in this post.

    In sum, Entity Framework does not include a client-side or hybrid query processor.

    MergeOption and Identity resolution

    There are chances that you have seen unsaved modifications in entities included in the results of queries. This is due to the fact that for tracked queries (i.e. if the query’s MergeOption is set to a value different from NoTracking) Entity Framework performs “identity resolution”.

    The process can be simply explained like this:

    1. The identity of each incoming entity is determined by building the corresponding EntityKey.
    2. The ObjectStateManager is looked up for an entity already present that has a matching EntityKey.
    3. If an entity with the same identity is already being tracked, the data coming from the server and the data already in the state manager are merged according to the MergeOption of the query.
    4. In the default case, MergeOption is AppendOnly, which means that the data of the entity in the state manager is left intact and is returned as part of the query results.

    However, membership of an entity in the results of a given query is decided exclusively based on the state existing on the server. In this example, for instance, what will the query get?:

    var customer1 = Customer.CreateCustomer(1, "Tiger");
    var customer2 = Customer.CreateCustomer(2, "Zombie");
    context.SaveChanges();
    customer1.LastName = "Zebra";
    var customer3 = Customer.CreateCustomer(100, "Zorro");
    context.AddObject("Customers", customer3);
    context.DeleteObject(customer2);
    var customerQuery = context.Customers
        .Where(c => c.LastName.StartsWith("Z"));
    foreach(var customer in customerQuery)
    {
        if (customer == customer1)
        {
            Console.WriteLine(c.LastName);
        }
    }

    The answer is:

    1. The modified entity customer1 won’t show up in the query because its LastName is still Tiger on the database.
    2. The deleted entity customer2 will be returned by the query, although it is a deleted entity already, because it still exists in the database.
    3. The new entity customer3 won’t make it, because it only exists in the local ObjectStateManager and not in the database.

    This behavior is by design and you need to be aware of it when writing your application.

    Put in some other way, if the units of work in your application follow a pattern in which they query first, then make modifications to entities and finally save them, discrepancies between query results and the contents of the ObjectSateManager cannot be observed.

    But as soon as queries are interleaved with modifications there is a chance that the server won’t contain an entity that exist in the state manager only and that that would match the predicate of the query. Those entities won’t be returned as part of the query.

    Notice that the chances that this happens has to do with how long lived is the Unit of Work in your application (i.e. how much does it take from the initial query to the call to SaveChanges).

    Hope this helps,
    Diego

Page 1 of 1 (2 items)