- Home
- LLBLGen Pro
- LLBLGen Pro Runtime Framework
setting LLBLGenProDataSource EntityCollection/EntityCollectionTypeName at runtime
Joined: 15-Nov-2007
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)
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";
}
Joined: 15-Nov-2007
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?
-
Can you post your aspx? I'm curious whether you are specifying DataContainerType and EntityCollectionTypeName at design time.
-
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.
Joined: 15-Nov-2007
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?
Joined: 15-Nov-2007
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?
Joined: 15-Nov-2007
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.
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.
Joined: 15-Nov-2007
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.
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. ?
Joined: 15-Nov-2007
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.
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.