-
Notifications
You must be signed in to change notification settings - Fork 3.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Query: Exception when filtering on nullable boolean value (through navigation property) #5454
Comments
@AustinFelipe could you provide some more info about the entities and DbContext you are using (specifically contents of class User, UserAvailability and OnModelCreating method of the DbContext) |
@maumar They are: public class ApplicationUser : IdentityUser
{
public string Name { get; set; }
public bool IsProfessional { get; set; }
public string PictureUrl { get; set; }
public Genre Genre {
get
{
return (Genre)GenreIdentifier;
}
set
{
GenreIdentifier = (int)value;
}
}
public int GenreIdentifier { get; set; }
/* Profile */
public string AboutMe { get; set; }
public ICollection<UserSpecialization> UserSpecs { get; set; }
public ICollection<UserResumeEntry> Resume { get; set; }
public ICollection<UserComment> Comments { get; set; }
public ICollection<UserTimetable> Timetable { get; set; }
public ICollection<UserAvailability> Availability { get; set; }
} public class UserAvailability
{
public int Id { get; set; }
public int Day { get; set; }
public string Hours { get; set; }
public ApplicationUser User { get; set; }
} public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public DbSet<Specialization> Specializations { get; set; }
public DbSet<Package> Packages { get; set; }
public DbSet<ChatMessage> ChatMessages { get; set; }
public DbSet<UserComment> UserComments { get; set; }
public DbSet<UserResumeEntry> UserResumes { get; set; }
public DbSet<UserTimetable> UserTimetables { get; set; }
public DbSet<UserAvailability> UserAvailabilities { get; set; }
public DbSet<UserVacation> UserVacations { get; set; }
public DbSet<Transaction> Transactions { get; set; }
public DbSet<TransactionItem> TransactionItems { get; set; }
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<UserSpecialization>()
.HasKey(t => new { t.ApplicationUserId, t.SpecializationId });
builder.Entity<ApplicationUser>()
.HasMany(t => t.Comments)
.WithOne(t => t.User);
builder.Entity<ApplicationUser>()
.HasMany(t => t.Timetable)
.WithOne(t => t.User);
builder.Entity<ApplicationUser>()
.Ignore(t => t.Genre);
builder.Entity<UserComment>()
.HasOne<ApplicationUser>(t => t.Client);
builder.Entity<UserTimetable>()
.HasOne<ApplicationUser>(t => t.Client);
builder.Entity<Transaction>()
.HasOne<ApplicationUser>(t => t.Buyer);
builder.Entity<Transaction>()
.HasOne<ApplicationUser>(t => t.Seller);
builder.Entity<UserTimetable>()
.HasOne<Transaction>(t => t.Transaction);
} Let me know if you need something |
@AustinFelipe the workaround is as follows: var availability = await context.UserAvailabilities
.Where(t => t.User.Id == user.Id && t.User.IsProfessional == true)
.ToList(); if that doesn't work you can also try: var availability = await context.UserAvailabilities
.Where(t => t.User.Id == user.Id && ((bool?)t.User.IsProfessional) == true)
.ToList(); The reason is that, since User is optional navigation and the result can be null, we are trying to compensate for that by casting non-nullable value types to their nullable version (so that in case User is null, we can return a null for the IsProfessional as well). In some cases we can be smart enough so that the process in transparent to the end user (and hopefully case will be transparent also), but sometimes users need to compensate for it manually. Good example would be projecting optional value into anonymous type: var availability = await context.UserAvailabilities
.Select(t => new { foo = t.User.IsProfessional })
.ToList(); will throw and we can't do anything about it, so it needs to be changed to: var availability = await context.UserAvailabilities
.Select(t => new { foo = (bool?)t.User.IsProfessional })
.ToList(); |
I think this is important still because it happens for a simple query and although the workaround (adding From another perspective it looks lower priority than other RTM bugs. @rowanmiller I have asked @maumar to still try to get a fix for this in but #3807 seems higher priority. Thoughts? |
Actually, this seems to be a duplicate of #4753 (scheduled for 1.0.1 currently) |
Agreed |
Problem was for happening for queries with optional navigations like so: context.Orders.Where(o => o.Customer.IsVip) In this case Customer can be nullable, so IsVip can also be nullable. We compensate for this by introducing the following: context.Orders.Where(o => o.Customer != null ? (bool?)o.Customer.IsVip : (bool?)null) However, we didn't convert it back to the original type (users had to introduce those casts themselves). Without the cast, those queries were throwing compile-time exceptions that were not very informative. Fix is to cast back to the original type requested by user: context.Orders.Where(o => (bool)(o.Customer != null ? (bool?)o.Customer.IsVip : (bool?)null)) This still may cause runtime errors if o.Customer is actually null, but those are much more understandable now. Also fixed compilation errors for complex Skip/Take arguments. Those are evaluated on the client for the time being.
Problem was for happening for queries with optional navigations like so: context.Orders.Where(o => o.Customer.IsVip) In this case Customer can be nullable, so IsVip can also be nullable. We compensate for this by introducing the following: context.Orders.Where(o => o.Customer != null ? (bool?)o.Customer.IsVip : (bool?)null) However, we didn't convert it back to the original type (users had to introduce those casts themselves). Without the cast, those queries were throwing compile-time exceptions that were not very informative. Fix is to cast back to the original type requested by user: context.Orders.Where(o => (bool)(o.Customer != null ? (bool?)o.Customer.IsVip : (bool?)null)) This still may cause runtime errors if o.Customer is actually null, but those are much more understandable now. Also fixed compilation errors for complex Skip/Take arguments. Those are evaluated on the client for the time being. CR: Andrew, Smit
fixed in 7fe8981 |
@maumar which means it's fixed for 1.0.0, right? |
@gdoron unfortunately by the time I got around to making the fix, RTM was pretty much locked for development, apart from security issues and ship stoppers. So this fix will actually be available in post RTM release. You can see the list of commits for RTM here: dev branch is not for post RTM development |
Problem was for happening for queries with optional navigations like so: context.Orders.Where(o => o.Customer.IsVip) In this case Customer can be nullable, so IsVip can also be nullable. We compensate for this by introducing the following: context.Orders.Where(o => o.Customer != null ? (bool?)o.Customer.IsVip : (bool?)null) However, we didn't convert it back to the original type (users had to introduce those casts themselves). Without the cast, those queries were throwing compile-time exceptions that were not very informative. Fix is to cast back to the original type requested by user: context.Orders.Where(o => (bool)(o.Customer != null ? (bool?)o.Customer.IsVip : (bool?)null)) This still may cause runtime errors if o.Customer is actually null, but those are much more understandable now. Also fixed compilation errors for complex Skip/Take arguments. Those are evaluated on the client for the time being. CR: Andrew, Smit
I'm trying to figure out why the following command doesn't work:
I'm getting this error (Sorry about the size...):
An unhandled exception has occurred while executing the request System.ArgumentException: Expression of type 'System.Func
2[Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor+TransparentIdentifier2[Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor+TransparentIdentifier
2[UPlatform.Models.UserAvailability,System.Collections.Generic.IAsyncEnumerable1[UPlatform.Models.ApplicationUser]],UPlatform.Models.ApplicationUser],System.Nullable
1[System.Boolean]]' cannot be used for parameter of type 'System.Func2[Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor+TransparentIdentifier
2[Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor+TransparentIdentifier2[UPlatform.Models.UserAvailability,System.Collections.Generic.IAsyncEnumerable
1[UPlatform.Models.ApplicationUser]],UPlatform.Models.ApplicationUser],System.Boolean]' of method 'System.Collections.Generic.IAsyncEnumerable1[Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor+TransparentIdentifier
2[Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor+TransparentIdentifier2[UPlatform.Models.UserAvailability,System.Collections.Generic.IAsyncEnumerable
1[UPlatform.Models.ApplicationUser]],UPlatform.Models.ApplicationUser]] _Where[TransparentIdentifier2](System.Collections.Generic.IAsyncEnumerable
1[Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor+TransparentIdentifier2[Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor+TransparentIdentifier
2[UPlatform.Models.UserAvailability,System.Collections.Generic.IAsyncEnumerable1[UPlatform.Models.ApplicationUser]],UPlatform.Models.ApplicationUser]], System.Func
2[Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor+TransparentIdentifier2[Microsoft.EntityFrameworkCore.Query.EntityQueryModelVisitor+TransparentIdentifier
2[UPlatform.Models.UserAvailability,System.Collections.Generic.IAsyncEnumerable1[UPlatform.Models.ApplicationUser]],UPlatform.Models.ApplicationUser],System.Boolean])'
I thought it was because the table is empty and there is some problem when I'm trying to use the t.User property, but isn't. Wherever I try to access the User property reference, I get this error.
This strange behaviour starts when I updated to 1.0.0-rc2 versions.
If I remove the
Where
clausure, it works:I already tried to replace
ToListAsync()
byToList()
, but I'm getting the same error.Any help will be welcome. 👍
Thanks
The text was updated successfully, but these errors were encountered: