aggregate function on dyanmic list with TypeConverter

Posts   
 
    
dcampbell
User
Posts: 3
Joined: 19-Mar-2007
# Posted on: 20-Jun-2007 18:22:35   

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

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39912
Joined: 17-Aug-2003
# Posted on: 20-Jun-2007 22:41:16   

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).

Frans Bouma | Lead developer LLBLGen Pro