views:

660

answers:

2

I have an asp:ListView control on an ASP.NET page. It is bound to an EntityDataSource which is setup this way:

<asp:EntityDataSource ID="EntityDataSourceOrders" runat="server" 
        ConnectionString="name=EntitiesContext" 
        DefaultContainerName="EntitiesContext" EntitySetName="SOrder" 
        Include="Address"
        EnableDelete="True" EnableInsert="True" 
        EnableUpdate="True">
</asp:EntityDataSource>

In SQL Server there are two tables, SOrder and Address. SOrder has a foreign key AddressID to the Address table ("an order has one address"). The address has an alphanumeric field "Name1".

In the LayoutTemplate of the ListView is a link button to sort the orders in the list by Name1 of the order's address:

<asp:LinkButton runat="server" ID="SortButtonName" Text="Name"
                CommandName="Sort" CommandArgument="Address.Name1" />

If I click this button I get an EntitySqlException telling me that "'Address.Name1' could not be resolved in the current context".

Sorting by a "flat" field of the order table - for instance "OrderCode" - works:

<asp:LinkButton runat="server" ID="SortButtonOrderCode" Text="Order number"
                CommandName="Sort" CommandArgument="OrderCode" />

So the exception occurs only when I try to sort by an related field in another table. I was expecting that with the Include="Address" property of the EntityDataSource sorting by fields of the related address should be possible, but it seems not.

I've made a test hack to check the query I expect the EntityDataSource to create internally:

With Linq to Entities:

using (EntitiesContext ctx = new EntitiesContext())
{
    var result = from order in ctx.SOrder.Include("Address")
                 orderby order.Address.Name1
                 select order;

    foreach (SOrder x in result)
    {
        string test=x.Address.Name1;
    }
}

Or with Entity SQL:

string queryString = @"SELECT VALUE x FROM SOrder AS x
                     Order By x.Address.Name1";
using (EntitiesContext ctx = new EntitiesContext())
{
    ObjectQuery<SOrder> query = 
        new ObjectQuery<SOrder>(queryString, ctx).Include("Address");

    foreach (SOrder x in query.Execute(MergeOption.AppendOnly))
    {
        string test=x.Address.Name1;
    }
}

Both works! I get a sorted result.

Now I am a bit lost how I get this sort operation working in the ListView. Does somebody have an idea what I am doing wrong here?

Thank you in advance!

A: 

Associated classes may wont work CommandArguments or eg DropDownList's DataTextField value. You may use Data Transfer Objects for listing and sorting

public class OrderDTO
{
    public string AddressName1 {get;set;}
}

using (EntitiesContext ctx = new EntitiesContext())
{
    var result = from order in ctx.SOrder
                 let dto = new OrderDTO
                            {
                                  AddressName1 = order.Address.Name1
                                  //bla bla
                            };
                 orderby dto.AddressName1
                 select dto;
}
cem
Thanks for your feedback! A question: Where do I have to place the code with the LINQ query? Inside of an event handler for the sort event? Or in the CommandName event? Do I need to assign the result of the LINQ query then to the ListView as a new DataSource?
Slauma
If you dont want to expression sort, Write to Page_Load and bind 'result' to your control. Or you want to sort automatic, so, grid can be sorted by all columns - as client request, I will write new code here just let me know.
cem
I've tested it already with a GridView. But it has basically the same problem because I cannot bind an associated field like "Address.Name1" to a column. I can use a template field with a label containing Eval("Address.Name1"). This works so far that the associated field is displayed in the grid. But declaratively in the template field I have to specify a SortExpression for sorting the column, and with "Address.Name1" in that SortExpression I get the same exception.
Slauma
I understand the idea to bind the "result" of the LINQ query to the ListView control in Page_Load. But then I lose all the built-in capabilities of the EntityDataSource - expecially paging and editing. It would force me to program all that stuff by hand which is something I'd like to avoid.
Slauma
As i said. You cannot use associated fields for some control actions. You have to deal with LinkButton's click or with your binding class. If you'll use dto, you may add ObjectDataSource to your page. It same as entitydatasource, sorting-listing but you have to write method and linq.
cem
Finally I found a (rather simple) solution, see my own answer to this question.
Slauma
A: 

I found the solution myself. It's all a matter of three missing characters: In my code above, this ...

<asp:LinkButton runat="server" ID="SortButtonName" Text="Name"
                CommandName="Sort" CommandArgument="Address.Name1" />

...is WRONG and has to be replaced by:

<asp:LinkButton runat="server" ID="SortButtonName" Text="Name"
                CommandName="Sort" CommandArgument="it.Address.Name1" />

Using "it." for properties of related objects seems to be necessary in contrast to flat fields. Therefore in the second example above both ways are possible:

CommandArgument="it.OrderCode" // works
CommandArgument="OrderCode"    // works as well

The same for "object identities" (primary key fields) of related objects:

CommandArgument="it.Address.AddressID" // works
CommandArgument="Address.AddressID"    // works as well

But again for related properties which are not identities:

CommandArgument="it.Address.Name1" // works
CommandArgument="Address.Name1"    // does NOT work

Crazy, the only place where I could find (accidentally) an indication to this solution, is this video:

How Do I Use the Entity Data Source?

...especially at around 9:40 min of the video.

Slauma