- Home
- LLBLGen Pro
- Bugs & Issues
Difference linq enum handling 2.6 <-> 3.5
Joined: 21-Apr-2011
After upgrading from 2.6 to 3.5 we noticed a subtle difference in enum handling in a linq query. An enum value is no longer evaluated to its value as an int but rather evaluated as a string with the value of its label. This behaviour shows when an enum value is used as a local variable, after removing the local variable and using the enum values directly from the enum the query works OK. But we are not happy with it. See the attachment for details, I do not have a simple repro.
Filename | File size | Added on | Approval |
---|---|---|---|
LLBLGenLocalEnum.txt | 25,476 | 01-May-2012 15:41.25 | Approved |
Joined: 21-Apr-2011
We use .NET 3.5 and the following LLBLGen dll's: - LinqSupport 2.6.11.0518, OrmSupport 2.6.12.0416 - LinqSupport 3.5.12.0317, OrmSupport 3.5.12.0416 The 2.6 version works fine (for us), the 3.5 gives problems.
We have found another, related, problem. Core is a situation where a property in the database (and LLBLGen entity) is an int and the application uses an Enum in the Linq query. The Enum value is not always casted to its int value, resulting in our problems.
We have troubles with V3.5 in the following 2 situations (see attached source):
- A Linq query resulting in a (dto) object where the property is also an int but is compared (= where clause) to an Enum value. This results in a SQL-abort (cannot cast int to string) because the parameter with the Enum value is no longer evaluated to its int value but to its Enum label:
- V2.6: ,@Entiteittype47=2,
-
V3.5: ,@p93=N'Magazijn', This problem is also found when a local declared (Enum) value is used to fill the dto object, when the Enum itself is used for filling the dto property it works ok.
-
A Linq query resulting in a (dto) object where the property is defnied as an Enum and casted from int to Enum when it is filled. This also gives trouble when the property is compared (= where clause) to an Enum. This results in a LLBLGen-abort: System.ArgumentNullException: Value cannot be null. Stack trace: [ArgumentNullException: Value cannot be null. Parameter name: conversionType] System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider) +9523496 System.Convert.ChangeType(Object value, Type conversionType) +32 SD.LLBLGen.Pro.ORMSupportClasses.DbSpecificCreatorBase.GetRealValue(Object currentValue, TypeConverter typeConverterToUse, Type actualDotNetType, Boolean valueIsEnumTyped) +51 SD.LLBLGen.Pro.ORMSupportClasses.DbSpecificCreatorBase.CreateParameter(IEntityFieldCore field, IFieldPersistenceInfo persistenceInfo, ParameterDirection direction, Object valueToSet) +53 SD.LLBLGen.Pro.ORMSupportClasses.FieldCompareValuePredicate.ToQueryText(Boolean inHavingClause) +438 SD.LLBLGen.Pro.ORMSupportClasses.PredicateExpression.ToQueryText(Boolean inHavingClause) +407 SD.LLBLGen.Pro.ORMSupportClasses.PredicateExpression.ToQueryText(Boolean inHavingClause) +407 SD.LLBLGen.Pro.ORMSupportClasses.PredicateExpression.ToQueryText(Boolean inHavingClause) +407 SD.LLBLGen.Pro.ORMSupportClasses.PredicateExpression.ToQueryText(Boolean inHavingClause) +407 SD.LLBLGen.Pro.ORMSupportClasses.PredicateExpression.ToQueryText(Boolean inHavingClause) +407 SD.LLBLGen.Pro.ORMSupportClasses.PredicateExpression.ToQueryText(Boolean inHavingClause) +407 SD.LLBLGen.Pro.ORMSupportClasses.PredicateExpression.ToQueryText() +12 SD.LLBLGen.Pro.ORMSupportClasses.DynamicQueryEngineBase.AppendWhereClause(IPredicate filter, QueryFragments destination, IQuery query) +60 SD.LLBLGen.Pro.DQE.SqlServer.DynamicQueryEngine.CreateSelectDQ(IEntityFieldCore[] selectList, IFieldPersistenceInfo[] fieldsPersistenceInfo, IRetrievalQuery query, IPredicate selectFilter, Int64 maxNumberOfItemsToReturn, ISortExpression sortClauses, IRelationCollection relationsToWalk, Boolean allowDuplicates, IGroupByCollection groupByClause, Boolean relationsSpecified, Boolean sortClausesSpecified) +1156 SD.LLBLGen.Pro.ORMSupportClasses.DynamicQueryEngineBase.CreateSelectDQ(IEntityFieldCore[] selectList, IFieldPersistenceInfo[] fieldsPersistenceInfo, DbConnection connectionToUse, IPredicate selectFilter, Int64 maxNumberOfItemsToReturn, ISortExpression sortClauses, IRelationCollection relationsToWalk, Boolean allowDuplicates, IGroupByCollection groupByClause) +492 SD.LLBLGen.Pro.DQE.SqlServer.DynamicQueryEngine.CreatePagingSelectDQ(IEntityFieldCore[] selectList, IFieldPersistenceInfo[] fieldsPersistenceInfo, DbConnection connectionToUse, IPredicate selectFilter, Int64 maxNumberOfItemsToReturn, ISortExpression sortClauses, IRelationCollection relationsToWalk, Boolean allowDuplicates, IGroupByCollection groupByClause, Int32 pageNumber, Int32 pageSize) +127 SD.LLBLGen.Pro.ORMSupportClasses.DynamicQueryEngineBase.CreateSelectDQ(IEntityFieldCore[] selectList, IFieldPersistenceInfo[] fieldsPersistenceInfo, DbConnection connectionToUse, IPredicate selectFilter, Int64 maxNumberOfItemsToReturn, ISortExpression sortClauses, IRelationCollection relationsToWalk, Boolean allowDuplicates, IGroupByCollection groupByClause, Int32 pageNumber, Int32 pageSize) +44 SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.CreateSelectDQ(IEntityFields2 fieldsToFetch, IFieldPersistenceInfo[] persistenceInfoObjects, IPredicateExpression filter, Int64 maxNumberOfItemsToReturn, ISortExpression sortClauses, IRelationCollection relationsToWalk, Boolean allowDuplicates, IGroupByCollection groupByClause, Int32 pageNumber, Int32 pageSize) +105 SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.CreateQueryFromElements(IEntityFields2 fieldCollectionToFetch, IRelationPredicateBucket filterBucket, Int32 maxNumberOfItemsToReturn, ISortExpression sortClauses, Boolean allowDuplicates, IGroupByCollection groupByClause, Int32 pageNumber, Int32 pageSize, IFieldPersistenceInfo[]& persistenceInfo, IRetrievalQuery& selectQuery) +91 SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchProjection(List
1 valueProjectors, IGeneralDataProjector projector, IEntityFields2 fields, IRelationPredicateBucket filter, Int32 maxNumberOfItemsToReturn, ISortExpression sortClauses, IGroupByCollection groupByClause, Boolean allowDuplicates, Int32 pageNumber, Int32 pageSize) +80 SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProvider2.ExecuteValueListProjection(QueryExpression toExecute) +363 SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.ExecuteExpression(Expression handledExpression) +230 SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.Execute(Expression expression) +23 SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.System.Linq.IQueryProvider.Execute(Expression expression) +21 SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProQuery
1.System.Collections.Generic.IEnumerable<T>.GetEnumerator() +18 System.Collections.Generic.List1..ctor(IEnumerable
1 collection) +315 System.Linq.Enumerable.ToList(IEnumerable1 source) +58 Piramide.Bis.Logistics.Business.TransportBO.GetList(Nullable
1 ClientId, Nullable1 ContractId, Nullable
1 TransportState, Nullable1 CreatedFrom, Nullable
1 CreatedUntil) +196 Piramide.Bis.Logistics.ManagementWeb.Controllers.TransportController.List(Nullable1 ClientId, Nullable
1 ContractId, Nullable1 TransportState, Nullable
1 CreatedFrom, Nullable`1 CreatedUntil) +141 lambda_method(Closure , ControllerBase , Object[] ) +245 System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters) +17
Filename | File size | Added on | Approval |
---|---|---|---|
Program.cs | 4,498 | 02-May-2012 12:41.39 | Approved |
This was a fix that was added on March 22nd, 2012.
Linq: When a query contained an entity in the projection and the entity contained a field with a type Nullable(Of Enum), it didn't convert the field's value to the proper enum but left it in the field as int32, causing a cast exception when the field was read on the entity.
The int value caused a cast exception.
Joined: 21-Apr-2011
I installed V3.5 in early april so I did not see the newest version. But also the newest version is no solution. Both our product and the repro (see attachment prior message) give errors on both variations. Our environment: - .NET 3.5 with the latest additions - LLBLGen Pro V3.5 20-4-2012 complete: - SD.LLBLGen.Pro.ORMSupportClasses.NET20.dll version 3.5.12.0417 - SD.LLBLGen.Pro.LinqSupportClasses.NET35.dll version 3.5.12.0317 - SD.LLBLGen.Pro.DQE.SqlServer.NET20.dll version 3.5.12.0317 - VS2010 with latest additions
The results of the repro: 1. Variation with int: Generated query (from sql-profiler): exec sp_executesql N'SELECT [LPA_L1].[TransportId], [LPA_L1].[TransportState], [LPA_L1].[Description], [LPA_L1].[Created] FROM (SELECT [LPLA_1].[TransportID] AS [TransportId], [LPLA_1].[TransportState], [LPLA_1].[Description], [LPLA_1].[Created] FROM [BisLogistics_Main].[dbo].[Transport] [LPLA_1] ) [LPA_L1] WHERE ( ( ( ( [LPA_L1].[TransportState] = @p2))))',N'@p2 nvarchar(10)',@p2=N'GoodResult'
The last parameter is the Enum's label not its value. This result:
$exception {System.Data.SqlClient.SqlException: Conversion failed when converting the nvarchar value 'GoodResult' to data type int.
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection)
at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj)
at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
at System.Data.SqlClient.SqlDataReader.HasMoreRows()
at System.Data.SqlClient.SqlDataReader.ReadInternal(Boolean setTimeout)
at System.Data.SqlClient.SqlDataReader.Read()
at SD.LLBLGen.Pro.ORMSupportClasses.ProjectionUtils.FetchProjectionFromReader(List1 valueProjectors, IGeneralDataProjector projector, IDataReader datasource, Int32 maxNumberOfItemsToReturn, Int32 pageNumber, Int32 pageSize, Boolean clientSideLimitation, Boolean clientSideDistinctFiltering, Boolean clientSidePaging, UniqueList
1 stringCache, Dictionary2 typeConvertersToRun)
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchProjection(List
1 valueProjectors, IGeneralDataProjector projector, IRetrievalQuery queryToExecute, Dictionary2 typeConvertersToRun)
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchProjection(List
1 valueProjectors, IGeneralDataProjector projector, IEntityFields2 fields, IRelationPredicateBucket filter, Int32 maxNumberOfItemsToReturn, ISortExpression sortClauses, IGroupByCollection groupByClause, Boolean allowDuplicates, Int32 pageNumber, Int32 pageSize)
at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProvider2.ExecuteValueListProjection(QueryExpression toExecute)
at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.ExecuteExpression(Expression handledExpression)
at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.Execute(Expression expression)
at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.System.Linq.IQueryProvider.Execute(Expression expression)
at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProQuery1.Execute()
at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProQuery
1.System.Collections.Generic.IEnumerable<T>.GetEnumerator()
at System.Collections.Generic.List1..ctor(IEnumerable
1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable1 source)
at LLBLGenEnumProperty.Program.DoQueryWhereInt(IDataAccessAdapter da, Nullable
1 transportState) in D:\Projects\LLBLGenEnumProperty\LLBLGenEnum35Property\Program.cs:line 76
at LLBLGenEnumProperty.Program.Main(String[] args) in D:\Projects\LLBLGenEnumProperty\LLBLGenEnum35Property\Program.cs:line 38} System.Exception {System.Data.SqlClient.SqlException}
2. Variation with Enum:
$exception {System.ArgumentNullException: Value cannot be null.
Parameter name: conversionType
at System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider)
at System.Convert.ChangeType(Object value, Type conversionType)
at SD.LLBLGen.Pro.ORMSupportClasses.DbSpecificCreatorBase.GetRealValue(Object currentValue, TypeConverter typeConverterToUse, Type actualDotNetType, Boolean valueIsEnumTyped)
at SD.LLBLGen.Pro.ORMSupportClasses.DbSpecificCreatorBase.CreateParameter(IEntityFieldCore field, IFieldPersistenceInfo persistenceInfo, ParameterDirection direction, Object valueToSet)
at SD.LLBLGen.Pro.ORMSupportClasses.FieldCompareValuePredicate.ToQueryText(Boolean inHavingClause)
at SD.LLBLGen.Pro.ORMSupportClasses.PredicateExpression.ToQueryText(Boolean inHavingClause)
at SD.LLBLGen.Pro.ORMSupportClasses.PredicateExpression.ToQueryText(Boolean inHavingClause)
at SD.LLBLGen.Pro.ORMSupportClasses.PredicateExpression.ToQueryText(Boolean inHavingClause)
at SD.LLBLGen.Pro.ORMSupportClasses.PredicateExpression.ToQueryText(Boolean inHavingClause)
at SD.LLBLGen.Pro.ORMSupportClasses.PredicateExpression.ToQueryText()
at SD.LLBLGen.Pro.ORMSupportClasses.DynamicQueryEngineBase.AppendWhereClause(IPredicate filter, QueryFragments destination, IQuery query)
at SD.LLBLGen.Pro.DQE.SqlServer.DynamicQueryEngine.CreateSelectDQ(IEntityFieldCore[] selectList, IFieldPersistenceInfo[] fieldsPersistenceInfo, IRetrievalQuery query, IPredicate selectFilter, Int64 maxNumberOfItemsToReturn, ISortExpression sortClauses, IRelationCollection relationsToWalk, Boolean allowDuplicates, IGroupByCollection groupByClause, Boolean relationsSpecified, Boolean sortClausesSpecified)
at SD.LLBLGen.Pro.ORMSupportClasses.DynamicQueryEngineBase.CreateSelectDQ(IEntityFieldCore[] selectList, IFieldPersistenceInfo[] fieldsPersistenceInfo, DbConnection connectionToUse, IPredicate selectFilter, Int64 maxNumberOfItemsToReturn, ISortExpression sortClauses, IRelationCollection relationsToWalk, Boolean allowDuplicates, IGroupByCollection groupByClause)
at SD.LLBLGen.Pro.DQE.SqlServer.DynamicQueryEngine.CreatePagingSelectDQ(IEntityFieldCore[] selectList, IFieldPersistenceInfo[] fieldsPersistenceInfo, DbConnection connectionToUse, IPredicate selectFilter, Int64 maxNumberOfItemsToReturn, ISortExpression sortClauses, IRelationCollection relationsToWalk, Boolean allowDuplicates, IGroupByCollection groupByClause, Int32 pageNumber, Int32 pageSize)
at SD.LLBLGen.Pro.ORMSupportClasses.DynamicQueryEngineBase.CreateSelectDQ(IEntityFieldCore[] selectList, IFieldPersistenceInfo[] fieldsPersistenceInfo, DbConnection connectionToUse, IPredicate selectFilter, Int64 maxNumberOfItemsToReturn, ISortExpression sortClauses, IRelationCollection relationsToWalk, Boolean allowDuplicates, IGroupByCollection groupByClause, Int32 pageNumber, Int32 pageSize)
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.CreateSelectDQ(IEntityFields2 fieldsToFetch, IFieldPersistenceInfo[] persistenceInfoObjects, IPredicateExpression filter, Int64 maxNumberOfItemsToReturn, ISortExpression sortClauses, IRelationCollection relationsToWalk, Boolean allowDuplicates, IGroupByCollection groupByClause, Int32 pageNumber, Int32 pageSize)
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.CreateQueryFromElements(IEntityFields2 fieldCollectionToFetch, IRelationPredicateBucket filterBucket, Int32 maxNumberOfItemsToReturn, ISortExpression sortClauses, Boolean allowDuplicates, IGroupByCollection groupByClause, Int32 pageNumber, Int32 pageSize, IFieldPersistenceInfo[]& persistenceInfo, IRetrievalQuery& selectQuery)
at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchProjection(List1 valueProjectors, IGeneralDataProjector projector, IEntityFields2 fields, IRelationPredicateBucket filter, Int32 maxNumberOfItemsToReturn, ISortExpression sortClauses, IGroupByCollection groupByClause, Boolean allowDuplicates, Int32 pageNumber, Int32 pageSize)
at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProvider2.ExecuteValueListProjection(QueryExpression toExecute)
at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.ExecuteExpression(Expression handledExpression)
at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.Execute(Expression expression)
at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.System.Linq.IQueryProvider.Execute(Expression expression)
at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProQuery
1.Execute()
at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProQuery1.System.Collections.Generic.IEnumerable<T>.GetEnumerator()
at System.Collections.Generic.List
1..ctor(IEnumerable1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable
1 source)
at LLBLGenEnumProperty.Program.DoQueryWhereEnum(IDataAccessAdapter da, Nullable`1 transportState) in D:\Projects\LLBLGenEnumProperty\LLBLGenEnum35Property\Program.cs:line 98
at LLBLGenEnumProperty.Program.Main(String[] args) in D:\Projects\LLBLGenEnumProperty\LLBLGenEnum35Property\Program.cs:line 46} System.Exception {System.ArgumentNullException}
A tremendous amount of information yet it's hardly usable to make a repro case. The program.cs you attached is fine, but without a database/entity model we can't reproduce anything.
For example:
LinqMetaData db = new LinqMetaData(da); // Copy the Enum value to a local field TransportStateValues loc = transportState.Value; var queryResult = from rl in db.Transport select new TransportDtoWithInt() { TransportId = rl.TransportId, TransportState = rl.TransportState, Description = rl.Description, Created = rl.Created }; if (transportState.HasValue) { queryResult = queryResult.Where(x => x.TransportState == (int) loc); }
In your where clause, you have x.TransportState== (int)loc... what type is TransportState? Nullable<int> ? Enum type?
I have no idea.
Please do: - create a simple table, 3 fields - create an entity on that table - create an enum type used in the entity - create a simple program with the SMALLEST linq queries possible which reproduce the behavior.
Pack the create script for the DB, the llblgen pro project file and the code file which reproduces the problem in a zip and attach it.
Otherwise we'll be hunting ghosts what might be wrong, as I have actually no idea: the problem comes from a specific situation, as other linq queries in our tests work fine with enums.
We have the dlls so we don't need them. All I need is this: - create a simple table, 3 fields - create an entity on that table - create an enum type used in the entity - create a simple program with the SMALLEST linq queries possible which reproduce the behavior.
so a script (DDL SQL), a llblgenproj file with the entity and code which defines the enum and the query which fails. No dlls, no binaries, no obj files, no resharper shit, just small files we can immediately use. Thanks.
About the first problem (the string instead of the value): the cause is that the (int) cast is not done in code, but it's been made part of the query due to the compiler. I don't know why this worked in v2.6 and not in v3, but as a lot has changed in v3's linq provider (for the good) I don't want to find out, just how to make it work on v3.
The linq provider simply skips the convert as it sees that it's a cast / convert from enum to int, which is an implicit cast. The problem however is that along the line, the enum value apparently isn't converted to int, but to string.
(edit) the reason it worked in v2.6 is actually 'lucky', as it passed the enum type to the parameter and let the parameter figure out how to deal with it. In v3, the enum is passed to the parameter but the type is set explicitly.
So what happens is the linq provider runs into Convert(enumValue, int32), and as it's an implicit cast (from enum to int32), it skips the Convert. Though this of course won't work, so it ends up problematic.
I've changed the behavior of the linq provider a bit so that it doesn't see a Convert(enumvalue, int32) as implicit anymore. This automatically ends up correct as it is properly handed later on. When I do this, both your queries work properly.
We now have to run all our tests again to see whether this indeed works OK or it broke something.
Fixed. Please check attached dll. Both queries should work now.
Btw, the two situations are rather edge-case, especially the second one, however I'm glad they're working now (again)
Filename | File size | Added on | Approval |
---|---|---|---|
SD.LLBLGen.Pro.LinqSupportClasses.NET35.zip | 91,207 | 03-May-2012 18:27.47 | Approved |
We're rolling back the fix for this, because of: http://www.llblgen.com/TinyForum/Messages.aspx?ThreadID=21034
We're working on a new fix for this issue, so both situations are fixed. We'll post here with the progress of this issue.
Situation 1, with DoQueryWhereInt, is fixed with a new approach. We're now working on situation 2, DoQueryWhereEnum, as that still fails.
We also fixed situation 2. All tests pass. So for you, nothing changes: when you download the new official build (see the thread linked above for a temp build), it will keep on working.
Keep in mind: the new fix is in both the linq support classes and the ormsupportclasses. So you have to update both.