GridView and related fields

Posts   
 
    
Posts: 12
Joined: 29-Oct-2009
# Posted on: 04-Nov-2009 10:58:03   

I'm having a similar problem as in http://www.llblgen.com/tinyforum/Messages.aspx?ThreadID=16798

I've been reading the manual and threads on this forum for the last couple of days, but I couldn't get this to work.

This is the code which works and I get a book named "Winter" in GridView (using LLBLGenDataSource2 as GridView's datasource).

I'm using the latest version of LLBLGen.


        protected void Page_Load(object sender, EventArgs e)
        {
            EntityCollection<BookEntity> orders = new EntityCollection<BookEntity>(new BookEntityFactory());
            IPrefetchPath2 prefetchPath = new PrefetchPath2((int)EntityType.BookEntity);
            prefetchPath.Add(BookEntity.PrefetchPathBookType);
            IRelationPredicateBucket filter = new RelationPredicateBucket();
            filter.PredicateExpression.Add(BookFields.Title == "Winter");
            DataAccessAdapter adapter = new DataAccessAdapter();
            adapter.FetchEntityCollection(orders, filter, prefetchPath);

            myDS.PrefetchPathToUse = prefetchPath;
            myDS.FilterToUse = filter;
            myDS.AdapterToUse = adapter;
        }


The last column called BookTypeId is a guid value. I have BookTypeEntity which has Id that represents BookTypeId from BookEntity.

So:

Table Book

Title ..... BookTypeId

Table BookType

Id Name

I would like gridview not to display Book.BookTypeId, but to display BookType.Name where Book.BookTypeId == BookType.Id. ( m:1 relation beetwene Book and BookTypeId using FK_Book_BookType) sorting works for all columns, so i would like sorting to work for this related column "Name" as well.

I also tried to add field BookFields.Title and field BookTypeFields.Name related by BookTypeFields.Id and BookFields.BookTypeId but I can't get the arguments right.


            ResultsetFields fields = new ResultsetFields(2);

            fields.DefineField(BookFields.Title, 0);
            fields.DefineField(new EntityField2("BookTypeName",
                new ScalarQueryExpression(BookTypeFields.Name.DbValue),
                (BookTypeFields.Id == BookFields.BookTypeId))), 1);

How can I update the code above to get this to work?

Thanks!

Walaa avatar
Walaa
Support Team
Posts: 14993
Joined: 21-Aug-2005
# Posted on: 04-Nov-2009 11:21:36   
'<%# DataBinder.Eval(Container.DataItem, "BookType.Name")%>'

For this and other examples and options please check the ASP.NET databinding solution in the download section of our website.

Posts: 12
Joined: 29-Oct-2009
# Posted on: 04-Nov-2009 12:07:04   

I implemented the code you gave me like this:


        <asp:GridView ID="GridView2" runat="server" AutoGenerateColumns="False" 
            DataKeyNames="Id" DataSourceID="LLBLGenProDataSource21">
            <Columns>
                <asp:BoundField DataField="Id" HeaderText="Id" ReadOnly="True" SortExpression="Id" />
                <asp:BoundField DataField="AuthorId" HeaderText="AuthorId" SortExpression="AuthorId" />
                <asp:BoundField DataField="Title" HeaderText="Title" SortExpression="Title" />
                <asp:BoundField DataField="IssuedOn" HeaderText="IssuedOn" SortExpression="IssuedOn" />
                <asp:BoundField DataField="RetailPrice" HeaderText="RetailPrice" SortExpression="RetailPrice" />
                <asp:BoundField DataField="BookTypeId" HeaderText="BookTypeId" SortExpression="BookTypeId" />
                <asp:TemplateField HeaderText="Name" ItemStyle-Width="150">
                    <ItemTemplate>
                       <asp:Label ID="Label77" runat="server" Text='<%# DataBinder.Eval(Container.DataItem, "BookType.Name")%>'></asp:Label>
                    </ItemTemplate>
                </asp:TemplateField>
            </Columns>
        </asp:GridView>

It doesn't work. I get all the values but the BookType.Name. I checked the download section but I have no idea what to do...

However, I managed to do something to get the Book.Title and BookType.Name:


            ResultsetFields fields = new ResultsetFields(2);

            fields.DefineField(BookFields.Title, 0);
            fields.DefineField(new EntityField2("Name",
                new ScalarQueryExpression(BookTypeFields.Name.SetAggregateFunction(AggregateFunction.None),
                (BookTypeFields.Id == BookFields.BookTypeId))), 1);
            
            DataTable results = new DataTable();
            DataAccessAdapter adapter = new DataAccessAdapter();

                adapter.FetchTypedList(fields, results,
                        null, 0,
                        new SortExpression(BookFields.Title | SortOperator.Ascending), true);           
        

            GridView1.DataSource = results;
            GridView1.DataBind();


I set SetAggregateFunction.None and now it shows BookTypeField.Name but when I try to sort based on BookTypeField.Name like this


                adapter.FetchTypedList(fields, results,
                        null, 0,
                        new SortExpression(BookTypeFields.Name| SortOperator.Ascending), true);         

I get this error:

The multi-part identifier "ACME.dbo.BookType.Name" could not be bound.

How can I get this to sort?

Thanks!

Walaa avatar
Walaa
Support Team
Posts: 14993
Joined: 21-Aug-2005
# Posted on: 04-Nov-2009 15:31:50   

My suggestion should be accompanied with the use of the apropriate prefetchPath in code behind.

fields.DefineField(BookFields.Title, 0); fields.DefineField(new EntityField2("Name", new ScalarQueryExpression(BookTypeFields.Name.SetAggregateFunction(AggregateFunction.None), (BookTypeFields.Id == BookFields.BookTypeId))), 1);

For your solution, why do you use a ScalarQueryExpression, you'd better just define the 2 fields and a relation to join the 2 tables.

Posts: 12
Joined: 29-Oct-2009
# Posted on: 04-Nov-2009 15:43:19   

Yes, this works... But now I can't sort this new field "Name". I guess I should not use ScalarQueryExpression.

Walaa avatar
Walaa
Support Team
Posts: 14993
Joined: 21-Aug-2005
# Posted on: 04-Nov-2009 15:51:33   

Yes it's not needed.

Posts: 12
Joined: 29-Oct-2009
# Posted on: 04-Nov-2009 16:28:21   

I've just managed something.


        protected void Page_Load(object sender, EventArgs e)
        {
            ResultsetFields fields = new ResultsetFields(2);

            fields.DefineField(BookFields.Title, 0);
            fields.DefineField(BookTypeFields.Name, 1);

            RelationPredicateBucket bookFilter = new RelationPredicateBucket();
            bookFilter.Relations.Add(BookEntity.Relations.BookTypeEntityUsingBookTypeId);
            bookFilter.PredicateExpression.Add(BookFields.BookTypeId == BookTypeFields.Id);
            
            DataTable results = new DataTable();
            DataAccessAdapter adapter = new DataAccessAdapter();

                adapter.FetchTypedList(fields, results,
                        bookFilter, 0,
                        new SortExpression(BookTypeFields.Name | SortOperator.Ascending), true);            
            
            GridView1.DataSource = results;
            GridView1.DataBind();

        }


One more problem. BookTypeId can be null and those fields are skipped by this code. How to include these fields with null values?

Thanks!!

Walaa avatar
Walaa
Support Team
Posts: 14993
Joined: 21-Aug-2005
# Posted on: 04-Nov-2009 16:57:58   
        bookFilter.Relations.Add(BookEntity.Relations.BookTypeEntityUsingBookTypeId);
        bookFilter.PredicateExpression.Add(BookFields.BookTypeId == BookTypeFields.Id);

First of all, you don't need the second line, as the criteria is specified automatically in the JOIN clause.

Second, you need a left join.

The following is what you should try:

bookFilter.Relations.Add(BookEntity.Relations.BookTypeEntityUsingBookTypeId, JoinHint.Left);
Posts: 12
Joined: 29-Oct-2009
# Posted on: 04-Nov-2009 20:00:01   

Your suggestion was most helpfull. I get Book.Title with blank BookType.Name! I thought the blank name was null but it seems that it is not. I would like "N/A" value instead of this empty field. I've accomplished this in another example using

<%# (Eval("FieldName") == null) ? "N/A" : Eval("FieldName") %>

for the current example with field BookType.Name it should be something like this

<%# (Eval("Name") == null) ? "N/A" : Eval("Name") %>

but it doesn't work for these empty strings, which should be null, but they are not.

I've tried this

<%# (Eval("Name").Equals("Poetry")) ? "N/A" : Eval("Name") %>

and it works fine, for Poetry there is now "N/A", and for everything else it shows the real name as it should.

The question is how to set value "N/A" instead of these empty fields I get in gridview.

This is HTML code:


        <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False">
            <Columns>
                <asp:TemplateField HeaderText="Title" ItemStyle-Width="350">
                    <ItemTemplate>
                       <asp:Label ID="Label1" runat="server" Text='<%# Eval("Title")%>'></asp:Label>
                    </ItemTemplate>
                </asp:TemplateField>
                
                <asp:TemplateField HeaderText="Name" ItemStyle-Width="150">
                    <ItemTemplate>
                       <asp:Label ID="Label2" runat="server" Text='<%# (Eval("Name") == null) ? "N/A" : Eval("Name") %>'></asp:Label>
                    </ItemTemplate>
                </asp:TemplateField>
            </Columns>
        </asp:GridView>

Code behind:


            ResultsetFields fields = new ResultsetFields(2);

            fields.DefineField(BookFields.Title, 0);
            fields.DefineField(BookTypeFields.Name, 1);

            RelationPredicateBucket bookFilter = new RelationPredicateBucket();   
            bookFilter.Relations.Add(BookEntity.Relations.BookTypeEntityUsingBookTypeId, JoinHint.Left);
            
            DataTable results = new DataTable();
            DataAccessAdapter adapter = new DataAccessAdapter();

                adapter.FetchTypedList(fields, results,
                        bookFilter, 0,
                        new SortExpression(BookTypeFields.Name | SortOperator.Ascending), true);            
            
            GridView1.DataSource = results;
            GridView1.DataBind();

Hopefully you can help me once again. Thanks!!

Edit:

I managed to do this with:


(Convert.ToString((Eval("Name"))).Equals("")) ? "N/A" : Eval("Name")

but it doesn't seem like a good idea, i'll leave it like this if noone has a better idea...

Thx!!

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 05-Nov-2009 04:37:13   

I haven't tested this, but I think you can combine these two declarative BoundField properties to manage your situation:

ConvertEmptyStringToNull http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.boundfield.convertemptystringtonull.aspx

NullDisplayText http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.boundfield.nulldisplaytext.aspx

David Elizondo | LLBLGen Support Team
Posts: 12
Joined: 29-Oct-2009
# Posted on: 05-Nov-2009 08:57:32   

I'm using a template field, which doesn't support nulldisplaytext, but the other link points to the solution that should work if the field really was null as should the code below, but no luck with both.


(Eval("Name") == null) ? "N/A" : Eval("Name")

It seems that the data fetched, that is null in the database, isn't null when it's fetched, so nulldisplaytext wouldn't work as the code above is not working...

Walaa avatar
Walaa
Support Team
Posts: 14993
Joined: 21-Aug-2005
# Posted on: 05-Nov-2009 10:53:06   

Not that it's simpler, but I'd have done it this way:

Text='<%#(string.IsNullOrEmpty(Eval("Title").ToString())) ? "N/A" : Eval("Title") %>'

(Edit) However if you want to let LLBLGen Pro return Null instead of empty string, then you have to set the following project property to false.

ConvertNulledReferenceTypesToDefaultValue When set to false (default is true), an entity field which has a reference type (e.g. string) will return null / Nothing if the value for the field is null / Nothing. When set to true (default), the default value belonging to that reference type is returned. The default value for a type is produced by the generated class TypeDefaultValue. A new project will inherit this value.