efcore.pg: Where queries with casted enum value throw Can't write CLR type
After upgrading to version 2.1 started getting errors with such queries:
var t = VehicleTypes.Truck;
context.Vehicles.Where(x => x.TypeId == (int)t)
throwing exception:
System.InvalidCastException: Can’t write CLR type VehicleTypes with handler type Int32Handler
If code is changed to:
int t = (int)VehicleTypes.Truck;
context.Vehicles.Where(x => x.TypeId == t)
it works.
I guess it’s because of new strict parameter type handling, but is this really the place for it trigger error? The enum value is supplied with a cast to correct type. Is this intended? It would require a lot of refactoring in our project.
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Comments: 32 (22 by maintainers)
If i understand what your are talking about, my 2 cents: this does not involve Postgres enums in any way. The DB column is a simple int column. The enum exsists only in C#, that is why it is casted to int when passed into expression. There are no enums in the DB.
@austindrenski done 😃
It looks like your project is using EF6, not EF Core.
Would you mind opening this issue with the repo for the Npgsql EF6 provider?
I looked into this, and the Microsoft.Data.Sqlite ADO.NET provider accepts CLR enums and unwraps them into ints (I assume the SQL Server provider does the same). So this has nothing to do with EF Core, which simply hands off the enum to the underlying ADO.NET provider; Npgsql just happens not to support this. Unless I’m mistaken that means the cast to int is actually sent to the server (haven’t inspected the SQL), and ends up doing nothing because the value is already an int.
We could think about making Npgsql (ADO.NET) do the same (as @austindrenski proposed above). This would mean the following behavior:
DataTypeName, Npgsql still sends the PostgreSQL enum.DataTypeName, Npgsql sends aninteger(orsmallintorbigint). This would be the new behavior.To be honest, this seems pretty confusing, and doesn’t fit with the strict, strongly-typed behavior that both PostgreSQL and Npgsql implement. We’re currently asking users to perform the cast (unwrapping the enum) outside the driver, rather than doing it for them inside the driver, just like we avoid doing other types of casts and conversions. It would also confuse first-time users who want to use PostgreSQL enums but getting integers (or integer-related errors) because they haven’t properly mapped or set
DataTypeName).I understand that this gets a bit more complicated with EF Core specifically, because unwrapping outside the driver is a bit less nice. But this still seems reasonable to me, and is unfortunately a small database portability issue.
I’m going to go ahead and close this for now, but am of course open to hearing opinions to the contrary.
@austindrenski Yes, it’s convention based foreign keys.
@roji Yes, it fixes the issue.
So it’s either changing to enums everywhere in the model or removing enums from queries.
I understand that this works as intended, but personally I can’t think of a reason when passing this cast operation to Postgres would make sense. Also, casting the enum directly (without variable):
(int)VehicleTypes.Suvdoes not throw. This is confusing in my opinion.I guess it was because db was modeled before the enum was introduced to make C# code clearer when “hardcoded” type id’s became required for business logic.
I will have to try it out then.