Reputation: 11
I'm using EntityFrameworkCore 7.0 in conjunction with Pomelo.EntityFrameworkCore.MySql to build a Code-First app relying on MySQL.
I'm using the Fluent Api and IEntityTypeConfiguration<> implementations for each and every entity class there is in my model assembly.
At one point, my DbContext declares this property:
public class SassyDbContext : DbContext
{
private DbSet<SassyUser> SassyUsers { get; set; }
}
It's supposed to expose objects of type SassyUser, defined as such :
public class SassyUser : IdentifiedObjectBase // abstract implementation of the Id PK.
{
private DateTime? _creationDate = null;
private static readonly byte[] __ = new byte[] { 0xd2, 0xc2, 0xd6, 0xcd, 0xb0, 0x1d, 0x4b, 0x80, 0xbe, 0x60, 0x54, 0x82, 0xb1, 0xfa, 0x9a, 0xc3 };
public SassyUser() { }
public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;
public string Pseudonym {
get => ModelUtils.DeScrambleString(__, PseudonymEnc);
set => PseudonymEnc = ModelUtils.ScrambleString(__, value);
}
private ICollection<AnnotatedAsset> AuthoredAssets { get; set; } = new HashSet<AnnotatedAsset>();
private ICollection<AnnotatedAsset> RevisedAssets { get; set; } = new HashSet<AnnotatedAsset>();
private ICollection<TraitBase> AuthoredTraits { get; set; } = new HashSet<TraitBase>();
private ICollection<TraitBase> RevisedTraits { get; set; } = new HashSet<TraitBase>();
private ICollection<AssetImportSession> ImportSessions { get; set; } = new HashSet<AssetImportSession>();
public string PseudonymEnc { get; set; } = string.Empty;
public string Password {
get => ModelUtils.DeScrambleString(__, PasswordEnc);
set => PasswordEnc = ModelUtils.ScrambleString(__, value);
}
public string PasswordEnc { get; set; } = string.Empty;
public string? Email {
get => ModelUtils.DeScrambleString(__, EmailEnc!);
set => EmailEnc = ModelUtils.ScrambleString(__, value!);
}
public string? EmailEnc { get; set; } = null;
public DateTime? CreationDate {
get {
if (_creationDate is null)
_creationDate = DateTime.Now.ToUniversalTime();
return _creationDate;
}
set {
if (value is not null)
_creationDate = value.Value.ToUniversalTime();
}
}
public Privileges GlobalPrivileges { get; set; } = Privileges.None;
public bool CanAddAssets(SassyDataset? dataset)
{
throw new NotImplementedException();
}
public bool CanEdit(AnnotatedAsset asset)
=> CanEdit(asset?.Id ?? null);
public bool CanEdit(long? assetId)
{
throw new NotImplementedException();
}
}
And here is its configurator implementation :
internal class SassyUserConfig : IEntityTypeConfiguration<SassyUser>
{
public SassyUserConfig() { }
public void Configure(EntityTypeBuilder<SassyUser> builder)
{
builder.Ignore(e => e.Pseudonym);
builder.Ignore(e => e.Password);
builder.Ignore(e => e.Email);
builder.Ignore("_creationDate");
builder
.Property(e => e.CreationDate)
.HasField("_creationDate")
.UsePropertyAccessMode(PropertyAccessMode.Property)
.IsRequired(true);
builder
.Property(e => e.EmailEnc)
.IsUnicode(false)
.HasColumnName("email")
.HasCharSet("ASCII")
.UseCollation("ascii_general_ci")
.IsRequired(false);
builder
.Property(e => e.FirstName)
.IsUnicode()
.IsRequired(false);
builder
.Property(e => e.LastName)
.IsUnicode()
.IsRequired(false);
builder
.Property(e => e.GlobalPrivileges)
.HasConversion<PrivilegeUlongConverter>()
.HasDefaultValue(Privileges.None)
.IsRequired(true);
builder
.Property(e => e.PseudonymEnc)
.IsUnicode(false)
.HasColumnName("jlgkj")
.HasCharSet("ASCII")
.UseCollation("ascii_general_ci")
.IsRequired();
builder
.Property(e => e.PasswordEnc)
.HasColumnName("fdsqqf")
.IsUnicode(false)
.HasCharSet("ASCII")
.UseCollation("ascii_general_ci")
.IsRequired();
builder
.UseTpcMappingStrategy()
.ToTable("SassyUsers");
}
}
But here's the problem... This DbContext method:
public SassyUser GetUser(string uide, string pwde)
=> (from usr in SassyUsers
where usr.PseudonymEnc == uide && usr.PasswordEnc == pwde
select usr).FirstOrDefault()!;
Throws an InvalidCastException upon return of the query, stating:
System.InvalidCastException
HResult=0x80004002
Message=Unable to cast object of type 'Sassy.Model.SassyDataset' to type 'Sassy.Model.SassyUser'.
Source=Microsoft.EntityFrameworkCore.Relational
StackTrace:
at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.Enumerator.MoveNext() in /_/src/EFCore.Relational/Query/Internal/SingleQueryingEnumerable.cs:line 179
at System.Linq.Enumerable.TryGetSingle[TSource](IEnumerable`1 source, Boolean& found) in /_/src/libraries/System.Linq/src/System/Linq/Single.cs:line 88
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.Execute[TResult](Expression query) in /_/src/EFCore/Query/Internal/QueryCompiler.cs:line 68
at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.Execute[TResult](Expression expression) in /_/src/EFCore/Query/Internal/EntityQueryProvider.cs:line 63
at Sassy.Model.SassyDbContext.GetUser(String uide, String pwde) in S:\sources\C#\Sassy_WPF\SassyDataModel\SassyDbContext.cs:line 262
at Sassy.AssetsExporter.ExportWorkhorse._worker_DoWork(Object sender, DoWorkEventArgs e) in S:\sources\C#\Sassy_WPF\SassyAssetExporter\ExportWorkhorse.cs:line 169
This exception was originally thrown at this call stack:
Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable<T>.Enumerator.MoveNext() in SingleQueryingEnumerable.cs
System.Linq.Enumerable.TryGetSingle<TSource>(System.Collections.Generic.IEnumerable<TSource>, out bool) in Single.cs
Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.Execute<TResult>(System.Linq.Expressions.Expression) in QueryCompiler.cs
Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.Execute<TResult>(System.Linq.Expressions.Expression) in EntityQueryProvider.cs
Sassy.Model.SassyDbContext.GetUser(string, string) in SassyDbContext.cs
Sassy.AssetsExporter.ExportWorkhorse._worker_DoWork(object, System.ComponentModel.DoWorkEventArgs) in ExportWorkhorse.cs
How by the devil should a DbSet<SassyUser>
try to cast its queries results to SassyDataset
when the query result is explicitely typed to SassyUser
?
None of those classes inherit each other. They both inherit IdentifiedObjectBase, which is an abstract class that declares and exposes one member, namely:
long? IdentifiedObjectBase.Id {get; set;}
I'm stumped ! Please help !
I fist thought I had mislabeled my tables... But nope... As you can see in the SassyUserConfig class, the table is explicitely named after the entity class 'SassyUser' -> 'SassyUsers' .
Upon successful creation of the schema after a quick SassyDbContext.Database.EnsureCreated(), I add a single SassyUser entity to the database. No the Db report a new row in the SassyUsers table. Nice.
But the DbSet<SassyUser> SassydbContext.SassyUsers
property keeps spouting InvalidCastExceptions upon query returns.
I also tried to call the SassydbContext.SassyUsers.ToArray()
extension method on the incriminated DbSet : same effect. The problem lies withing a misnaming inside the model itself.
Here's a debug trace of EF on this :
dbug: 26/06/2023 21:45:07.048 CoreEventId.QueryCompilationStarting[10111] (Microsoft.EntityFrameworkCore.Query)
Compiling query expression:
'DbSet<SassyUser>()'
dbug: 26/06/2023 21:45:07.058 CoreEventId.QueryExecutionPlanned[10107] (Microsoft.EntityFrameworkCore.Query)
Generated query execution expression:
'queryContext => new SingleQueryingEnumerable<SassyUser>(
(RelationalQueryContext)queryContext,
RelationalCommandCache.QueryExpression(
Projection Mapping:
EmptyProjectionMember -> Dictionary<IProperty, int> { [Property: IdentifiedObjectBase.Id (long?) Required PK AfterSave:Throw ValueGenerated.OnAdd, 0], [Property: SassyUser.CreationDate (_creationDate, DateTime?) Required PropertyAccessMode.Property, 1], [Property: SassyUser.EmailEnc (string) Ansi, 2], [Property: SassyUser.FirstName (string), 3], [Property: SassyUser.GlobalPrivileges (Privileges) Required ValueGenerated.OnAdd, 4], [Property: SassyUser.LastName (string), 5], [Property: SassyUser.PasswordEnc (string) Required Ansi, 6], [Property: SassyUser.PseudonymEnc (string) Required Ansi, 7] }
SELECT s.Id, s.CreationDate, s.email, s.FirstName, s.GlobalPrivileges, s.LastName, s.fdsqqf, s.jlgkj
FROM SassyUsers AS s),
ReaderColumn[] { ReaderColumn<long>, ReaderColumn<DateTime>, ReaderColumn<object>, ReaderColumn<object>, ReaderColumn<ulong>, ReaderColumn<object>, ReaderColumn<object>, ReaderColumn<object> },
Func<QueryContext, DbDataReader, ResultContext, SingleQueryResultCoordinator, SassyUser>,
Sassy.Model.SassyDbContext,
False,
True,
True
)'
dbug: 26/06/2023 21:45:07.061 RelationalEventId.CommandCreating[20103] (Microsoft.EntityFrameworkCore.Database.Command)
Creating DbCommand for 'ExecuteReader'.
dbug: 26/06/2023 21:45:07.064 RelationalEventId.CommandCreated[20104] (Microsoft.EntityFrameworkCore.Database.Command)
Created DbCommand for 'ExecuteReader' (3ms).
dbug: 26/06/2023 21:45:07.067 RelationalEventId.CommandInitialized[20106] (Microsoft.EntityFrameworkCore.Database.Command)
Initialized DbCommand for 'ExecuteReader' (5ms).
dbug: 26/06/2023 21:45:07.069 RelationalEventId.ConnectionOpening[20000] (Microsoft.EntityFrameworkCore.Database.Connection)
Opening connection to database 'sassyDb' on server 'localhost'.
dbug: 26/06/2023 21:45:07.072 RelationalEventId.ConnectionOpened[20001] (Microsoft.EntityFrameworkCore.Database.Connection)
Opened connection to database 'sassyDb' on server 'localhost'.
dbug: 26/06/2023 21:45:07.074 RelationalEventId.CommandExecuting[20100] (Microsoft.EntityFrameworkCore.Database.Command)
Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT `s`.`Id`, `s`.`CreationDate`, `s`.`email`, `s`.`FirstName`, `s`.`GlobalPrivileges`, `s`.`LastName`, `s`.`fdsqqf`, `s`.`jlgkj`
FROM `SassyUsers` AS `s`
info: 26/06/2023 21:45:07.076 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
Executed DbCommand (2ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT `s`.`Id`, `s`.`CreationDate`, `s`.`email`, `s`.`FirstName`, `s`.`GlobalPrivileges`, `s`.`LastName`, `s`.`fdsqqf`, `s`.`jlgkj`
FROM `SassyUsers` AS `s`
Exception thrown: 'System.InvalidCastException' in System.Private.CoreLib.dll
'SassyAssetsExporter.exe' (CoreCLR: clrhost): Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\7.0.5\System.Reflection.Metadata.dll'. Symbols loaded.
fail: 26/06/2023 21:45:07.131 CoreEventId.QueryIterationFailed[10100] (Microsoft.EntityFrameworkCore.Query)
An exception occurred while iterating over the results of a query for context type 'Sassy.Model.SassyDbContext'.
System.InvalidCastException: Unable to cast object of type 'Sassy.Model.SassyDataset' to type 'Sassy.Model.SassyUser'.
at lambda_method247(Closure, QueryContext, DbDataReader, ResultContext, SingleQueryResultCoordinator)
at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.Enumerator.MoveNext()
Exception thrown: 'System.InvalidCastException' in Microsoft.EntityFrameworkCore.Relational.dll
dbug: 26/06/2023 21:45:07.162 RelationalEventId.DataReaderClosing[20301] (Microsoft.EntityFrameworkCore.Database.Command)
Closing data reader to 'sassyDb' on server 'localhost'.
dbug: 26/06/2023 21:45:07.164 RelationalEventId.DataReaderDisposing[20300] (Microsoft.EntityFrameworkCore.Database.Command)
A data reader for 'sassyDb' on server 'localhost' is being disposed after spending 83ms reading results.
dbug: 26/06/2023 21:45:07.166 RelationalEventId.ConnectionClosing[20002] (Microsoft.EntityFrameworkCore.Database.Connection)
Closing connection to database 'sassyDb' on server 'localhost'.
dbug: 26/06/2023 21:45:07.168 RelationalEventId.ConnectionClosed[20003] (Microsoft.EntityFrameworkCore.Database.Connection)
Closed connection to database 'sassyDb' on server 'localhost' (2ms).
Why would this "lambda_method247" would return a SassyDataset entity ? Did EF try a failsafe cast to another Entity type because the SassyUser entity was impossible to populate ? What ? Huh ? My head hurts.
Even the SQL query displayed in the debug output seems okay !
Is there a way to sniff out what EF is doing under the hood with the DbSet query ?
Upvotes: 0
Views: 66