- Home
- LLBLGen Pro
- LLBLGen Pro Runtime Framework
aggregate function on dyanmic list with TypeConverter
Joined: 19-Mar-2007
We're running into an interesting problem when attempting to apply the CountDistinct aggregate function onto a column that has a TypeConverter. What we want to do is this:
...
ResultsetFields fields = new ResultsetFields(1);
fields.DefineField(MediaFields.MediaId, 0);
fields[0].SetAggregateFunction(AggregateFunction.CountDistinct);
DataTable dt = new DataTable();
using (DataAccessAdapter adapter = new DataAccessAdapter())
{
adapter.FetchTypedList(fields, dt, filter);
adapter.FetchEntityCollection(ec, filter, 0, sort, path, pageNumber, pageSize);
}
...
MediaFields.MediaId is a RAW column which we convert to GUID using a TypeConverter.
However we get this exception when fetching the typed list:
MyEverything.Community.UnitTests.BLL.XmlDataProviderTests.GetShowEpisodes_UserContentShow : System.NotSupportedException : Conversion from a value of type 'System.Decimal' isn't supported
at Parse3.Data.Oracle.TypeConverters.GuidConverter.ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, Object value)
at System.ComponentModel.TypeConverter.ConvertFrom(Object value)
at SD.LLBLGen.Pro.ORMSupportClasses.DataTableFiller.Fill(IDataReader dataSource, DataTable toFill, IFieldPersistenceInfo[] fieldsPersistenceInfo, IRetrievalQuery queryExecuted)
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.ExecuteMultiRowDataTableRetrievalQuery(IRetrievalQuery queryToExecute, DbDataAdapter dataAdapterToUse, DataTable tableToFill, IFieldPersistenceInfo[] fieldsPersistenceInfo)
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchTypedList(IEntityFields2 fieldCollectionToFetch, DataTable dataTableToFill, IRelationPredicateBucket filterBucket, Int32 maxNumberOfItemsToReturn, ISortExpression sortClauses, Boolean allowDuplicates, IGroupByCollection groupByClause, Int32 pageNumber, Int32 pageSize)
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchTypedList(IEntityFields2 fieldCollectionToFetch, DataTable dataTableToFill, IRelationPredicateBucket filterBucket)
at MyEverything.Community.BLL.XmlFeedDataProvider.GetShowEpisodesInternal(Guid showId, Guid channelId, Boolean showMature, Int32 pageNumber, Int32 pageSize, Int32 sortBy, Int32 sortDirection) in C:\Projects\MyEverything\code\community\trunk\BLL\XmlFeedDataProvider.cs:line 481
at MyEverything.Community.BLL.XmlFeedDataProvider.GetShowEpisodes(Guid showId, Guid channelId, Nullable`1 userId, Int32 pageNumber, Int32 pageSize, Int32 sortBy, Int32 sortDirection) in C:\Projects\MyEverything\code\community\trunk\BLL\XmlFeedDataProvider.cs:line 97
at MyEverything.Community.UnitTests.BLL.XmlDataProviderTests.GetShowEpisodes_UserContentShow() in C:\Projects\MyEverything\code\community\trunk\UnitTests\BLL\XmlDataProviderTests.cs:line 214
We then changed the code to:
ResultsetFields fields = new ResultsetFields(1);
fields.DefineField(MediaFields.MediaIdInt, 0);
fields[0].SetAggregateFunction(AggregateFunction.CountDistinct);
DataTable dt = new DataTable();
using (DataAccessAdapter adapter = new DataAccessAdapter())
{
adapter.FetchTypedList(fields, dt, filter);
adapter.FetchEntityCollection(ec, filter, 0, sort, path, pageNumber, pageSize);
}
MediaFields.MediaIdInt is a NUMBER(10, 0) which is type converted to an Int32. This returns the proper count and throws no exception.
As the stack trace indicates it's going through the TypeConverter and attempting to convert the number value to a guid.
Is this by design or possibly a defect? Are we missing something and just using the generated code/TypeConverter wrong?
The code for the TypeConverter is:
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
System.Guid? returnValue = new System.Guid?();
if (value != null)
{
switch (value.GetType().FullName)
{
case "System.Byte[]":
returnValue = new Guid((System.Byte[])value);
break;
case "System.String":
string sValue = (string)value;
if (sValue.Length == 32)
{
byte[] bytes = new byte[16];
for (int i = 0; i < 16; i++)
{
bytes[i] = byte.Parse(sValue.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber);
}
returnValue = new Guid(bytes);
}
else if (sValue.Length == 36)
{
returnValue = new Guid(sValue);
}
else
{
throw new NotSupportedException("Unrecognized string format");
}
break;
case "System.DBNull":
break;
default:
throw new NotSupportedException("Conversion from a value of type '" + value.GetType().ToString() + "' isn't supported");
}
}
return returnValue;
}
Thanks!
LLBLGen Pro version + buildnr: 2.0.0.0 Final (Oct 23, 2006) Runtime library version: 2.0.0.61023 .NET version: 2.0 Oracle 10g
Although a column can be of type RAW, an aggregate on that will be of a different type, namely Decimal (depends on the aggregate). This is the case in all databases. So the value comes back and is of type decimal and the typeconverter goes bezerk.
I can understand why this is: the type converter is converting raw to guid, however the aggregate isn't the field value, it's the result of the aggregate function, so the type converter shouldn't be used.
One way to work around this is by using an expression field I think, so: fields.DefineField(new EntityField2("NumberOfIds", new Expression(MediaFields.MediaId), AggregateFunction.CountDistinct), 0);
Another is to extend the converter to accept decimals and simply pass back the same value (so no conversion).