setting LLBLGenProDataSource EntityCollection/EntityCollectionTypeName at runtime

Posts   
 
    
trevorg
User
Posts: 104
Joined: 15-Nov-2007
# Posted on: 10-Mar-2010 10:17:07   

I am trying to create a quick and dirty screen for editing lookup tables....user selects lookup table name from a dropdown, select * from the table populates a GridView (with AutoGenerateColumns = True)

I'm having trouble getting the LLBLGenProDataSource to select from the new table....I am changing the EntityCollectionTypeName property only, not the EntityCollection property. When I do so and Databind the grid, the generated SQL query logging shows it always selects from the same table.

ASPX:

llblgenpro:LLBLGenProDataSource ID="llbDataSource" runat="server" LivePersistence="True" 
        DataContainerType="EntityCollection">
    </llblgenpro:LLBLGenProDataSource>
<br />
<llblgenpro:LLBLGenProDataSource ID="llbLookupTables" runat="server" 
        DataContainerType="EntityCollection" 
        EntityCollectionTypeName="Talisman.CorpScorecard.Domain.CollectionClasses.SysTableCollection, Talisman.CorpScorecard.Domain">
    </llblgenpro:LLBLGenProDataSource>
<br />

<asp:DropDownList ID="ddlLookupTable" runat="server" DataSourceID="llbLookupTables" DataValueField="TableName" DataTextField="TableName" AutoPostBack="True"></asp:DropDownList>

<asp:GridView CssClass="gridView" DataSourceID="llbDataSource" AlternatingRowStyle-CssClass="even" ID="grid" 
        runat="server" AutoGenerateColumns="True" 
        AutoGenerateEditButton="True" AutoGenerateDeleteButton="True" 
        AutoGenerateSelectButton="True" EmptyDataText="No Data found">
<AlternatingRowStyle CssClass="even"></AlternatingRowStyle>
    </asp:GridView>

CODE:

    Private Sub ddlLookupTable_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles ddlLookupTable.SelectedIndexChanged
        Dim typeName As String = String.Format("Talisman.CorpScorecard.Domain.CollectionClasses.{0}Collection, Talisman.CorpScorecard.Domain", ddlLookupTable.SelectedValue)
        Debug.WriteLine("TypeName = " & typeName)
        Me.llbDataSource.EntityCollectionTypeName = typeName    'does this affect anything??
        'Me.llbDataSource.EntityCollection =    'is this what I actually have to set??
        Me.llbDataSource.Select()
        Me.llbDataSource.Refetch = True
        Me.llbDataSource.DataBind()
        'Me.grid.DataKeyNames=  'TODO: This will have to be read from an instantiated instance of the selected LLBLGen collection Type
        'Me.grid.DataKeyNames = New String() {"CategoryCode"}
        Me.grid.DataBind()
    End Sub

Is it possible to do this, in this manner? Or, do I actually have to set EntityCollection itself (in which case I'd have to do a CreateInstance using reflection I suppose)

Walaa avatar
Walaa
Support Team
Posts: 14946
Joined: 21-Aug-2005
# Posted on: 10-Mar-2010 11:05:50   

Try to set the EntityCollectionTypeName in the PageLoad event handler. There you can check on the selected value of the dropDownList.

trevorg
User
Posts: 104
Joined: 15-Nov-2007
# Posted on: 10-Mar-2010 15:36:52   

Same result.

I have a feeling this property doesn't actually do anything?

Walaa avatar
Walaa
Support Team
Posts: 14946
Joined: 21-Aug-2005
# Posted on: 10-Mar-2010 18:43:04   

I'm sorry I was mistaken. The page controls get loaded before the PageLoad event. So you need to use PageInit instead.

The following works for me:

    <llblgenpro:LLBLGenProDataSource ID="LLBLGenProDataSource1" runat="server" 
        DataContainerType="EntityCollection" >
    </llblgenpro:LLBLGenProDataSource>
    <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="True" 
        DataSourceID="LLBLGenProDataSource1">
    </asp:GridView>
        protected void Page_Init(object sender, EventArgs e)
        {
            LLBLGenProDataSource1.EntityCollectionTypeName =
                "Northwind.CollectionClasses.CustomersCollection, Northwind";
        }
trevorg
User
Posts: 104
Joined: 15-Nov-2007
# Posted on: 10-Mar-2010 18:56:10   

1.

LLBLGenProDataSource1.EntityCollectionTypeName =
                "EntityCollectionTypeName=\"Northwind.CollectionClasses.ProductsCollection, Northwind\"";

This seems very strange to me....are you sure that is the exact code you are running?

So, you are including within the value you are setting the EntityCollectionTypeName property to, a "re-statement" of the EntityCollectionTypeName.

This seems absolutely bizarre to me....can you confirm that this is in fact correct?

  1. Can you post your aspx? I'm curious whether you are specifying DataContainerType and EntityCollectionTypeName at design time.

  2. Looking at the LLB runtime code, the only interesting things I've come across is:

public class LLBLGenProDataSourceView : LLBLGenProDataSourceViewBase, IDisposable
        public string EntityCollectionTypeName
        {
            get
            {
                if(_containedCollection == null)
                {
                    if(this.Owner.DataContainerType == DataSourceDataContainerType.EntityCollection)
                    {
                        return base.DataContainerTypeName;
                    }
                    else
                    {
                        return string.Empty;
                    }
                }
                else
                {
                    return FieldUtilities.CreateFullTypeName(_containedCollection.GetType());
                }
            }
            set
            {
                if(value.Length <= 0)
                {
                    _containedCollection = null;
                    return;
                }
                bool differentType = (this.EntityCollectionTypeName != value);
                base.DataContainerTypeName = value;
                if(differentType)
                {
                    CreateEntityCollectionContainerIfRequired();
                }
            }
        }


        private void CreateEntityCollectionContainerIfRequired()
        {
// DOES THIS LINE OF CODE MAKE SENSE????:
            if(_containedCollection == null)
            {
                Type typeToCreate = Type.GetType(base.DataContainerTypeName);
                if(typeToCreate != null)
                {
                    this.ContainedEntityCollection = (IEntityCollection)Activator.CreateInstance(typeToCreate);
                }
            }
        }

The

if(_containedCollection == null)

looks like it might be able to cause a problem.

Walaa avatar
Walaa
Support Team
Posts: 14946
Joined: 21-Aug-2005
# Posted on: 10-Mar-2010 18:57:37   

You managed to see that post before I've deleted it simple_smile This was a copy and paste error. I've corrected the code, please go back to my previous message.

trevorg
User
Posts: 104
Joined: 15-Nov-2007
# Posted on: 10-Mar-2010 19:13:37   

Ok, that works for me too. However, I am trying to change this in the SelectedIndexChanged event of a dropdownlist.

In there, when this executes:

Dim typeName As String = String.Format("Talisman.CorpScorecard.Domain.CollectionClasses.{0}Collection, Talisman.CorpScorecard.Domain", ddlLookupTable.SelectedValue)
Me.llbDataSource.EntityCollectionTypeName = typeName    'does this even affect anything??

And I check the value of Me.llbDataSource.EntityCollectionTypeName, it hasn't changed!

And I can't do it on page init, as ddlLookupTable.SelectedValue has no value at that point.

Why would my setting of a new value to llbDataSource.EntityCollectionTypeName be ignored like this?

trevorg
User
Posts: 104
Joined: 15-Nov-2007
# Posted on: 10-Mar-2010 19:56:24   

Ok, this works in the ddlLookupTable_SelectedIndexChanged event:

Dim typeName As String = String.Format("Talisman.CorpScorecard.Domain.CollectionClasses.{0}Collection, Talisman.CorpScorecard.Domain", ddlLookupTable.SelectedValue)
Me.llbDataSource.EntityCollection = Nothing
Me.llbDataSource.EntityCollectionTypeName = typeName    'Now this works!!!!

So, imho, the

if(_containedCollection == null)

I mentioned earlier is a bit of a bug.

bool differentType = (this.EntityCollectionTypeName != value);

If differentType is True, it should always reinstantiate ContainedEntityCollection, regardless if _containedCollection == null or not, because the Type has changed.

Thoughts?

trevorg
User
Posts: 104
Joined: 15-Nov-2007
# Posted on: 10-Mar-2010 22:55:26   

In case anyone else encounters this, this is the code that I finally got working (to change the Entity Collection Type at runtime for a LLBLGenProDataSource bound to a GridView)....there are also some issues with the GridView itself, so the order you do things in is important:

    Private Sub ddlLookupTable_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles ddlLookupTable.SelectedIndexChanged
        Dim typeName As String = String.Format("Talisman.CorpScorecard.Domain.CollectionClasses.{0}Collection, Talisman.CorpScorecard.Domain", ddlLookupTable.SelectedValue)
        Me.llbDataSource.EntityCollection = Nothing
        Me.llbDataSource.EntityCollectionTypeName = typeName    
        Me.llbDataSource.Refetch = True
        Me.grid.Columns.Clear()
        Me.grid.DataKeyNames = Nothing
        Me.grid.DataBind()
        If Me.llbDataSource.EntityCollection IsNot Nothing AndAlso Me.llbDataSource.EntityCollection.Count > 0 Then
            Dim dataKeyNames As String = ""
            For Each fld As IEntityField In Me.llbDataSource.EntityCollection(0).PrimaryKeyFields
                If dataKeyNames <> "" Then dataKeyNames &= ","
                dataKeyNames &= fld.Name
            Next
            Me.grid.DataKeyNames = dataKeyNames.Split(",".ToCharArray())
        End If
    End Sub

NOTE: I'd still be interested to hear Frans' opinion on the if(_containedCollection == null) issue.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 11-Mar-2010 10:27:08   

The problem I think is ... the order in which events happen in a webpage postback (but I'm not sure, this is one of the many issues with writing a datasourcecontrol: there are so many things happening in a what seem a random order, that it's hard to track down when what has to happen to make everything working, as there's no clear documentation on this). When the page posts back (e.g. due to an event), there are several things happening, for example the controls are re-instantiated, filled again with their state from the viewstate/control state, the data bound in the datasource is loaded etc.

What I think happens is that you change things, but the bound grid already read the data from the datasource in a page event which happened before your index changed event. So you can then change the datasource, but the grid won't notice.

In webpages, datasources don't trigger bound controls that they have to refresh themselves like in winforms, it's the other way around: a bound control asks the datasourcecontrol to give it data based on a set of parameters (e.g. page number). The datasourcecontrol gives the data, the bound control (e.g. gridview) will render itself and things move on. That's also why you have to clear the grid to make things happening, you effectively make it bind twice.

Frans Bouma | Lead developer LLBLGen Pro
trevorg
User
Posts: 104
Joined: 15-Nov-2007
# Posted on: 11-Mar-2010 15:34:57   

Frans,

Exactly, which is why it took me so long to figure this out, half the problem was with the gridview.

But, what do you think of the issue I noted in the LLB runtime code though (see my earlier posts)...I think thats a bug.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 12-Mar-2010 10:26:58   

Could you post again please in short what the problem is, because it's now scattered around multiple posts which are also written before things became clear it was an event order problem. ?

Frans Bouma | Lead developer LLBLGen Pro
trevorg
User
Posts: 104
Joined: 15-Nov-2007
# Posted on: 12-Mar-2010 16:48:08   

Oh sorry.....will paste together the relevant parts.....

When I run this:

Dim typeName As String = String.Format("Talisman.CorpScorecard.Domain.CollectionClasses.{0}Collection, Talisman.CorpScorecard.Domain", ddlLookupTable.SelectedValue)
Me.llbDataSource.EntityCollectionTypeName = typeName    'Has no effect!!

Because....

public class LLBLGenProDataSourceView : LLBLGenProDataSourceViewBase, IDisposable
        public string EntityCollectionTypeName
        {
            get
            {
                if(_containedCollection == null)
                {
                    if(this.Owner.DataContainerType == DataSourceDataContainerType.EntityCollection)
                    {
                        return base.DataContainerTypeName;
                    }
                    else
                    {
                        return string.Empty;
                    }
                }
                else
                {
                    return FieldUtilities.CreateFullTypeName(_containedCollection.GetType());
                }
            }
            set
            {
                if(value.Length <= 0)
                {
                    _containedCollection = null;
                    return;
                }
                bool differentType = (this.EntityCollectionTypeName != value);
                base.DataContainerTypeName = value;
                if(differentType)
                {
                    CreateEntityCollectionContainerIfRequired();
                }
            }
        }


        private void CreateEntityCollectionContainerIfRequired()
        {
// DOES THIS LINE OF CODE MAKE SENSE????:
            if(_containedCollection == null)
            {
                Type typeToCreate = Type.GetType(base.DataContainerTypeName);
                if(typeToCreate != null)
                {
                    this.ContainedEntityCollection = (IEntityCollection)Activator.CreateInstance(typeToCreate);
                }
            }
        }

So, imho, the Code:

if(_containedCollection == null) 

Seems to me to be a bit of a bug.

bool differentType = (this.EntityCollectionTypeName != value); 

If differentType is True, it should always reinstantiate ContainedEntityCollection, regardless if _containedCollection == null or not, because the Type has changed. CreateEntityCollectionContainerIfRequired is assuming no Type change, and if it already has a ContainedEntityCollection, it doesn't reload.

As I workaround, I am manually setting the collection to null to get it to work, but that is unintuitive, the only way I figured out to do it was by looking at the LLB runtime source code.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 15-Mar-2010 10:11:22   

The thing is that your code, of overwriting the type, will be executed after the state has already been retrieved back from the page. It's really a problem of when what's executed, and also: the datasource control doesn't know what's coming, so it acts on what it knows, i.e. read state back from the cache if present etc.

Datasources are really about 2-way databinding. If you are databinding things dynamically and then IMHO not for 2-way databinding (otherwise cache management becomes really complicated with the page events being executed in a given order interleaved with your code), just bind the data to the control, and be done with it. simple_smile

Frans Bouma | Lead developer LLBLGen Pro