- Home
- LLBLGen Pro
- Bugs & Issues
Can't ClientSide Sort with Fields mapped on related fields if null key?
Joined: 22-Dec-2006
Using 2.0.0.0, runtime 2.0.0.60911, Self-service NET2.0, SQL Server 2005...
I have a GridView that is DataBind at runtime to a LLBLGenProDataSource. I am referencing a "fields mapped on related fields"...
<asp:TemplateField HeaderText="License Status" SortExpression="StatusName">
<ItemTemplate>
<asp:Label ID="Label5" runat="server" Text='<%# Bind("StatusName") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
StatusName is really in another table -> Status.. the LLBLGenProDataSource is set to SortingMode="ClientSide", which was the only way I was able to get "fields mapped on related fields" working (???).
The error (bug?) is that if I have a row in the GridView that has NULL for StatusId... in other words it has no link to any StatusName... and I click to sort that column, I will get a small hang and then a "Server Application Unavailable" webpage. Event Viewer shows a ".NET Runtime 2.0 Error Reporting" with description of "Faulting application aspnet_wp.exe, version 2.0.50727.210, stamp 45063b16, faulting module kernel32.dll, version 5.1.2600.2945, stamp 44ab9a84, debug? 0, fault address 0x00012a5b."...
Can you assist? Thanks!
Darrell Bircsak
Would you please post the complete exception text and Stack trace?
Also there was a sorting bug that was solved some time ago, would you please download and use the latest release of the LLBLGen Pro runtimeLibraries.
Thanks.
Joined: 22-Dec-2006
Ok, I just got latest... using DQE.SqlServer.NET20.dll ver 2.0.0.61120 and ORMSupportClasses.NET20 2.0.0.61205.
I still get a crash (timeout) when I try to sort a GridView column that is data bound to an LLBLGenProDataSource control. More specifically, the crash happens when a column is a "fields mapped on related fields" and there is a NULL value for one or more of the values in the column. (If I assign something to each, then there is no crash.)
The only crash data I can get back is from Event Log which reports: "Faulting application aspnet_wp.exe, version 2.0.50727.210, stamp 45063b16, faulting module kernel32.dll, version 5.1.2600.2945, stamp 44ab9a84, debug? 0, fault address 0x00012a5b."
0000: 41 00 70 00 70 00 6c 00 A.p.p.l.
0008: 69 00 63 00 61 00 74 00 i.c.a.t.
0010: 69 00 6f 00 6e 00 20 00 i.o.n. .
0018: 46 00 61 00 69 00 6c 00 F.a.i.l.
0020: 75 00 72 00 65 00 20 00 u.r.e. .
0028: 20 00 61 00 73 00 70 00 .a.s.p.
0030: 6e 00 65 00 74 00 5f 00 n.e.t._.
0038: 77 00 70 00 2e 00 65 00 w.p...e.
0040: 78 00 65 00 20 00 32 00 x.e. .2.
0048: 2e 00 30 00 2e 00 35 00 ..0...5.
0050: 30 00 37 00 32 00 37 00 0.7.2.7.
0058: 2e 00 32 00 31 00 30 00 ..2.1.0.
0060: 20 00 34 00 35 00 30 00 .4.5.0.
0068: 36 00 33 00 62 00 31 00 6.3.b.1.
0070: 36 00 20 00 69 00 6e 00 6. .i.n.
0078: 20 00 6b 00 65 00 72 00 .k.e.r.
0080: 6e 00 65 00 6c 00 33 00 n.e.l.3.
0088: 32 00 2e 00 64 00 6c 00 2...d.l.
0090: 6c 00 20 00 35 00 2e 00 l. .5...
0098: 31 00 2e 00 32 00 36 00 1...2.6.
00a0: 30 00 30 00 2e 00 32 00 0.0...2.
00a8: 39 00 34 00 35 00 20 00 9.4.5. .
00b0: 34 00 34 00 61 00 62 00 4.4.a.b.
00b8: 39 00 61 00 38 00 34 00 9.a.8.4.
00c0: 20 00 66 00 44 00 65 00 .f.D.e.
00c8: 62 00 75 00 67 00 20 00 b.u.g. .
00d0: 30 00 20 00 61 00 74 00 0. .a.t.
00d8: 20 00 6f 00 66 00 66 00 .o.f.f.
00e0: 73 00 65 00 74 00 20 00 s.e.t. .
00e8: 30 00 30 00 30 00 31 00 0.0.0.1.
00f0: 32 00 61 00 35 00 62 00 2.a.5.b.
00f8: 0d 00 0a 00 ....
That's some good readin'!
Darrell
Could you switch on error reporting in the web.config? You should get the error in the browser, together with the stacktrace.
Also, teh field mapped on related field, is that a field mapped in the designer? (as that code checks for null's) or is that a property you added yourself? If so, could you post that code?
Joined: 22-Dec-2006
Okay, sorry... I forgot you could do that. The "Server Application Unavailable" Error webpage was something I thought was different. The exception was:
System.StackOverflowException was unhandled - {Cannot evaluate expression because the current thread is in a stack overflow state.}
It bombs on some generated code:
/// <summary>Returns a new IEntityRelation object, between GeneralAgentContactEntity and DepartmentEntity over the m:1 relation they have, using the relation between the fields:
/// GeneralAgentContact.DepartmentId - Department.DepartmentId
/// </summary>
public virtual IEntityRelation DepartmentEntityUsingDepartmentId
{
get
{
IEntityRelation relation = new EntityRelation(SD.LLBLGen.Pro.ORMSupportClasses.RelationType.ManyToOne);
relation.StartEntityIsPkSide = false;
(BOMBS HERE) relation.AddEntityFieldPair(EntityFieldFactory.Create(DepartmentFieldIndex.DepartmentId), EntityFieldFactory.Create(GeneralAgentContactFieldIndex.DepartmentId));
relation.InheritanceInfoPkSideEntity = InheritanceInfoProviderSingleton.GetInstance().GetInheritanceInfo("DepartmentEntity", false);
relation.InheritanceInfoFkSideEntity = InheritanceInfoProviderSingleton.GetInstance().GetInheritanceInfo("GeneralAgentContactEntity", true);
return relation;
}
}
DepartmentId can be null (in the DB). If it isn't null, it doesn't bomb. I mapped Department.Name as "DepartmentName" so that I could display GeneralAgentContact.DepartmentName in the GridView & sort on that field. This "field mapped on related field" was set up in the LLBLGen Pro designer.
<asp:TemplateField HeaderText="Department" SortExpression="DepartmentName">
<ItemTemplate>
<asp:Label ID="Label1" runat="server" Text='<%# Bind("DepartmentName") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
Same kind of crash happens if I try to sort on any other FieldMappedOnRelatedField that is Null. Thanks for assistance!
EDIT: wait, maybe I need to update my Designer! I only updated my runtime .dlls... I will need to wait 'till tomorrow to try that...
Darrell Bircsak
Joined: 22-Dec-2006
It repeats over and over
...
LicAppDal.DLL!LicAppDal.EntityClasses.GeneralAgentContactEntity.Department.get() Line 1381 + 0xd bytes C#
LicAppDal.DLL!LicAppDal.EntityClasses.GeneralAgentContactEntity.DepartmentName.get() Line 1531 + 0xa bytes C#
[External Code]
LicAppDal.DLL!LicAppDal.EntityClasses.GeneralAgentContactEntity.DesetupSyncDepartment(bool signalRelatedEntity = false, bool resetFKFields = true) Line 987 + 0x8c bytes C#
LicAppDal.DLL!LicAppDal.EntityClasses.GeneralAgentContactEntity.UnsetRelatedEntity(SD.LLBLGen.Pro.ORMSupportClasses.IEntity relatedEntity = {LicAppDal.EntityClasses.DepartmentEntity}, string fieldName = "Department", bool signalRelatedEntityManyToOne = true) Line 328 + 0xb bytes C#
[External Code]
LicAppDal.DLL!LicAppDal.EntityClasses.DepartmentEntity.UnsetRelatedEntity(SD.LLBLGen.Pro.ORMSupportClasses.IEntity relatedEntity = {LicAppDal.EntityClasses.GeneralAgentContactEntity}, string fieldName = "GeneralAgentContact", bool signalRelatedEntityManyToOne = true) Line 442 + 0x14 bytes C#
[External Code]
LicAppDal.DLL!LicAppDal.EntityClasses.GeneralAgentContactEntity.DesetupSyncDepartment(bool signalRelatedEntity = true, bool resetFKFields = true) Line 987 + 0x8c bytes C#
LicAppDal.DLL!LicAppDal.EntityClasses.GeneralAgentContactEntity.SetupSyncDepartment(SD.LLBLGen.Pro.ORMSupportClasses.IEntity relatedEntity = {LicAppDal.EntityClasses.DepartmentEntity}) Line 995 + 0xf bytes C#
LicAppDal.DLL!LicAppDal.EntityClasses.GeneralAgentContactEntity.SetRelatedEntity(SD.LLBLGen.Pro.ORMSupportClasses.IEntity relatedEntity = {LicAppDal.EntityClasses.DepartmentEntity}, string fieldName = "Department") Line 296 + 0xa bytes C#
[External Code]
LicAppDal.DLL!LicAppDal.EntityClasses.DepartmentEntity.SetRelatedEntity(SD.LLBLGen.Pro.ORMSupportClasses.IEntity relatedEntity = {LicAppDal.EntityClasses.GeneralAgentContactEntity}, string fieldName = "GeneralAgentContact") Line 409 + 0x2e bytes C#
LicAppDal.DLL!LicAppDal.EntityClasses.GeneralAgentContactEntity.Department.set(LicAppDal.EntityClasses.DepartmentEntity value = {LicAppDal.EntityClasses.DepartmentEntity}) Line 1399 + 0x10 bytes C#
LicAppDal.DLL!LicAppDal.EntityClasses.GeneralAgentContactEntity.GetSingleDepartment(bool forceFetch = false) Line 703 + 0xe bytes C#
LicAppDal.DLL!LicAppDal.EntityClasses.GeneralAgentContactEntity.Department.get() Line 1381 + 0xd bytes C#
LicAppDal.DLL!LicAppDal.EntityClasses.GeneralAgentContactEntity.DepartmentName.get() Line 1531 + 0xa bytes C#
[External Code]
Here's the path through the code... the THIS HERE means it steps into that point... and the next lines of code are where it went.
/// <summary> Gets / Sets the value of the related field this.Department.Name.</summary>
public virtual System.String DepartmentName
{
get
{
// THIS HERE !!!!!!!!!!!!!!!!!!!!!!!!!
DepartmentEntity relatedEntity = this.Department;
if(relatedEntity!=null)
{
return relatedEntity.Name;
}
else
{
return (System.String)TypeDefaultValue.GetDefaultValue(typeof(System.String));
}
}
set
{
DepartmentEntity relatedEntity = this.Department;
if(relatedEntity!=null)
{
relatedEntity.Name = value;
}
}
}
..........
/// <summary> Gets / sets related entity of type 'DepartmentEntity'. This property is not visible in databound grids.
/// Setting this property to a new object will make the load-on-demand feature to stop fetching data from the database, until you set this
/// property to null. Setting this property to an entity will make sure that FK-PK relations are synchronized when appropriate.</summary>
/// <remarks>This property is added for conveniance, however it is recommeded to use the method 'GetSingleDepartment()', because
/// this property is rather expensive and a method tells the user to cache the result when it has to be used more than once in the
/// same scope. The property is marked non-browsable to make it hidden in bound controls, f.e. datagrids.</remarks>
[Browsable(false)]
public virtual DepartmentEntity Department
{
// THIS HERE !!!!!!!!!!!!!!!!!!!!!!!!!
get { return GetSingleDepartment(false); }
..........
public virtual DepartmentEntity GetSingleDepartment(bool forceFetch)
{
if( ( !_alreadyFetchedDepartment || forceFetch || _alwaysFetchDepartment) && !base.IsSerializing && !base.IsDeserializing )
{
DepartmentEntity newEntity = new DepartmentEntity();
if(base.ParticipatesInTransaction)
{
base.Transaction.Add(newEntity);
}
bool fetchResult = false;
if(base.CheckIfLazyLoadingShouldOccur(GeneralAgentContactEntity.Relations.DepartmentEntityUsingDepartmentId))
{
fetchResult = newEntity.FetchUsingPK(this.DepartmentId.GetValueOrDefault());
}
if(!_departmentReturnsNewIfNotFound && !fetchResult)
{
this.Department = null;
}
else
{
if((base.ActiveContext!=null)&&fetchResult)
{
newEntity = (DepartmentEntity)base.ActiveContext.Get(newEntity);
}
// THIS HERE !!!!!!!!!!!!!!!!!!!!!!!!!
this.Department = newEntity;
_alreadyFetchedDepartment = fetchResult;
}
if(base.ParticipatesInTransaction && !fetchResult)
{
base.Transaction.Remove(newEntity);
}
}
return _department;
}
..........
/// <summary> Gets / sets related entity of type 'DepartmentEntity'. This property is not visible in databound grids.
/// Setting this property to a new object will make the load-on-demand feature to stop fetching data from the database, until you set this
/// property to null. Setting this property to an entity will make sure that FK-PK relations are synchronized when appropriate.</summary>
/// <remarks>This property is added for conveniance, however it is recommeded to use the method 'GetSingleDepartment()', because
/// this property is rather expensive and a method tells the user to cache the result when it has to be used more than once in the
/// same scope. The property is marked non-browsable to make it hidden in bound controls, f.e. datagrids.</remarks>
[Browsable(false)]
public virtual DepartmentEntity Department
{
get { return GetSingleDepartment(false); }
set
{
if(base.IsDeserializing)
{
SetupSyncDepartment(value);
}
else
{
if(value==null)
{
if(_department != null)
{
_department.UnsetRelatedEntity(this, "GeneralAgentContact");
}
}
else
{
// THIS HERE !!!!!!!!!!!!!!!!!!!!!!!!!
((IEntity)value).SetRelatedEntity(this, "GeneralAgentContact");
}
}
}
}
I hope this helps..
Darrell Bircsak
P.S. I just installed latest 2.0.0.0 Designer with libraries... and get same result.
Be sure the webapp indeed has the LATEST runtimes in the bin folder. ASP.NET compile cycles sometimes don't refresh assemblies the way you want / expect.
The entities which dive into the endless loop, do they reference eachother perhaps?
Scratch that, that's not relevant.
I'll try to reproduce this. The sort on field-mapped-onto-related-field, indeed has to be done client-side.
Joined: 22-Dec-2006
Okay, I just started with a fresh new database with two tables... Person and Department table. Person table has foreign key to Department but it can be null. Made a new LLBLGen project and had Person table have a "field mapped onto related field" of Department.Name called DepartmentName.
Now put up an LLBLGenProDataSource and point a GridView at it. Try to sort on DepartmentName and crash....call stack:
[External Code]
> SortBugDal.DLL!SortBugDal.EntityClasses.DepartmentEntity.SetRelatedEntity(SD.LLBLGen.Pro.ORMSupportClasses.IEntity relatedEntity = {SortBugDal.EntityClasses.PersonEntity}, string fieldName = "Person") Line 235 + 0x32 bytes C#
SortBugDal.DLL!SortBugDal.EntityClasses.PersonEntity.Department.set(SortBugDal.EntityClasses.DepartmentEntity value = {SortBugDal.EntityClasses.DepartmentEntity}) Line 834 + 0x10 bytes C#
SortBugDal.DLL!SortBugDal.EntityClasses.PersonEntity.GetSingleDepartment(bool forceFetch = false) Line 460 + 0xd bytes C#
SortBugDal.DLL!SortBugDal.EntityClasses.PersonEntity.Department.get() Line 816 + 0xd bytes C#
SortBugDal.DLL!SortBugDal.EntityClasses.PersonEntity.DepartmentName.get() Line 866 + 0xb bytes C#
[External Code]
SortBugDal.DLL!SortBugDal.EntityClasses.PersonEntity.DesetupSyncDepartment(bool signalRelatedEntity = false, bool resetFKFields = true) Line 627 + 0x89 bytes C#
SortBugDal.DLL!SortBugDal.EntityClasses.PersonEntity.UnsetRelatedEntity(SD.LLBLGen.Pro.ORMSupportClasses.IEntity relatedEntity = {SortBugDal.EntityClasses.DepartmentEntity}, string fieldName = "Department", bool signalRelatedEntityManyToOne = true) Line 258 + 0xb bytes C#
[External Code]
SortBugDal.DLL!SortBugDal.EntityClasses.DepartmentEntity.UnsetRelatedEntity(SD.LLBLGen.Pro.ORMSupportClasses.IEntity relatedEntity = {SortBugDal.EntityClasses.PersonEntity}, string fieldName = "Person", bool signalRelatedEntityManyToOne = true) Line 256 + 0x17 bytes C#
[External Code]
SortBugDal.DLL!SortBugDal.EntityClasses.PersonEntity.DesetupSyncDepartment(bool signalRelatedEntity = true, bool resetFKFields = true) Line 627 + 0x89 bytes C#
SortBugDal.DLL!SortBugDal.EntityClasses.PersonEntity.SetupSyncDepartment(SD.LLBLGen.Pro.ORMSupportClasses.IEntity relatedEntity = {SortBugDal.EntityClasses.DepartmentEntity}) Line 635 + 0xe bytes C#
SortBugDal.DLL!SortBugDal.EntityClasses.PersonEntity.SetRelatedEntity(SD.LLBLGen.Pro.ORMSupportClasses.IEntity relatedEntity = {SortBugDal.EntityClasses.DepartmentEntity}, string fieldName = "Department") Line 237 + 0x9 bytes C#
[External Code]
SortBugDal.DLL!SortBugDal.EntityClasses.DepartmentEntity.SetRelatedEntity(SD.LLBLGen.Pro.ORMSupportClasses.IEntity relatedEntity = {SortBugDal.EntityClasses.PersonEntity}, string fieldName = "Person") Line 235 + 0x32 bytes C#
SortBugDal.DLL!SortBugDal.EntityClasses.PersonEntity.Department.set(SortBugDal.EntityClasses.DepartmentEntity value = {SortBugDal.EntityClasses.DepartmentEntity}) Line 834 + 0x10 bytes C#
..... repeats ....
/// <summary> Sets the internal parameter related to the fieldname passed to the instance relatedEntity. </summary>
/// <param name="relatedEntity">Instance to set as the related entity of type entityType</param>
/// <param name="fieldName">Name of field mapped onto the relation which resolves in the instance relatedEntity</param>
[EditorBrowsable(EditorBrowsableState.Never)]
public override void SetRelatedEntity(IEntity relatedEntity, string fieldName)
{
switch(fieldName)
{
case "Person":
// STACK OVERFLOW HERE !! :(
_person.Add((PersonEntity)relatedEntity);
OnRelatedEntitySet(relatedEntity, fieldName);
break;
default:
break;
}
}
I searched my entire hard drive to make sure every SD.LLBLGen.Pro.*.DLL was latest.
EDIT: It's funny, I can sort correctly on the DepartmentId (with NULLs)... just not on the related field.
Darrell Bircsak
I have a small repro now, which reproduces the lack of a sort though doesn't throw the exception. I'll see if I can modify the repro to meet your exact specifics to see if I can make it throw the exception as well.
I can reproduce both: the stack overflow and the sort failure.
The stack overflow happens when you sort on another column and then the field mapped onto related field.
The field mapped onto related field should be sortable with SortingMode="ClientSide", set on the datasourcecontrol, however that has no effect.
Looking into it. (note: it does work on adapter, so it's a bit strange why this doesn't work on selfservicing)
(edit). I know why it does work on adapter, and it also works on selfservicing: use a prefetch path.
In our example, we have a simple grid and two entities; Dep and Emp. Emp has a field mapped onto related fields, DepName, which is the name of the related Dep. The grid loads all Emp's.
To efficiently load such a grid, it's highly recommended you use a prefetch path, because every row will otherwise trigger lazy loading and will load the entity manually.
First, set the llblgenprodatasource control's SortMode to ClientSide, as you're going to sort on a field which is in another entity (or a custom property you added yourself).
then, set the prefetch path, as in the example below.
Page:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebApplication1._Default" %>
<%@ Register Assembly="SD.LLBLGen.Pro.ORMSupportClasses" Namespace="SD.LLBLGen.Pro.ORMSupportClasses"
TagPrefix="llblgenpro" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<llblgenpro:LLBLGenProDataSource ID="LLBLGenProDataSource1" runat="server" DataContainerType="EntityCollection"
EntityCollectionTypeName="NS.CollectionClasses.EmpCollection, NS" SortingMode="clientSide" >
</llblgenpro:LLBLGenProDataSource>
</div>
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataKeyNames="Id"
DataSourceID="LLBLGenProDataSource1" AllowSorting="True">
<Columns>
<asp:BoundField DataField="Id" HeaderText="Id" InsertVisible="False" ReadOnly="True"
SortExpression="Id" />
<asp:BoundField DataField="Name" HeaderText="Name" SortExpression="Name" />
<asp:BoundField DataField="DepId" HeaderText="DepId" SortExpression="DepId" />
<asp:BoundField DataField="DepName" HeaderText="DepName" SortExpression="DepName" />
</Columns>
</asp:GridView>
</form>
</body>
</html>
code behind:
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using SD.LLBLGen.Pro.ORMSupportClasses;
using NS;
using NS.EntityClasses;
namespace WebApplication1
{
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if(!Page.IsPostBack)
{
PrefetchPath path = new PrefetchPath((int)EntityType.EmpEntity);
path.Add(EmpEntity.PrefetchPathDep);
LLBLGenProDataSource1.PrefetchPathToUse = path;
}
}
}
}
The datasourcecontrol will cache the set path, so you just have to set it once.
I still would like to know why it's dying in the infinite loop, though this will get you up and running: you have a more efficient grid and it now also works
It's caused by the fact that lazy loading for 'null' FK's (which don't return a related entity) by default returns a NEW entity. This is tossed away but makes the grid confused. The datasource control correctly sorts the view and returns the sorted entity view. The grid however tries to pull the related entities again and this goes wrong somehow. You can thus also work around this by setting the project property (in the llblgen pro designer) LazyLoadingWithoutResultReturnsNew to false (default is true).
I'll now see why the empty new entity isn't tossed away but causes havok inside the code.
Joined: 22-Dec-2006
Cool... I feel like I helped someone else make the world a better place.
I'm really impressed by how your team jumped into this. Thank you for the support & patience!
Yes, I've been putting off prefetching....
Darrell Bircsak