diff --git a/src/EFCore.Relational/Storage/RelationalTypeMapping.cs b/src/EFCore.Relational/Storage/RelationalTypeMapping.cs
index cd4d7813860..1098802c9cf 100644
--- a/src/EFCore.Relational/Storage/RelationalTypeMapping.cs
+++ b/src/EFCore.Relational/Storage/RelationalTypeMapping.cs
@@ -434,6 +434,8 @@ public virtual DbParameter CreateParameter(
parameter.Direction = ParameterDirection.Input;
parameter.ParameterName = name;
+ value = ConvertUnderlyingEnumValueToEnum(value);
+
if (Converter != null)
{
value = Converter.ConvertToProvider(value);
@@ -456,6 +458,15 @@ public virtual DbParameter CreateParameter(
return parameter;
}
+ // Enum when compared to constant will always have constant of integral type
+ // when enum would contain convert node. We remove the convert node but we also
+ // need to convert the integral value to enum value.
+ // This allows us to use converter on enum value or print enum value directly if supported by provider
+ private object ConvertUnderlyingEnumValueToEnum(object value)
+ => value?.GetType().IsInteger() == true && ClrType.UnwrapNullableType().IsEnum
+ ? Enum.ToObject(ClrType.UnwrapNullableType(), value)
+ : value;
+
///
/// Configures type information of a .
///
@@ -473,15 +484,7 @@ protected virtual void ConfigureParameter([NotNull] DbParameter parameter)
///
public virtual string GenerateSqlLiteral([CanBeNull] object value)
{
- // Enum when compared to constant will always have constant of integral type
- // when enum would contain convert node. We remove the convert node but we also
- // need to convert the integral value to enum value.
- // This allows us to use converter on enum value or print enum value directly if supported by provider
- if (value?.GetType().IsInteger() == true
- && ClrType.UnwrapNullableType().IsEnum)
- {
- value = Enum.ToObject(ClrType.UnwrapNullableType(), value);
- }
+ value = ConvertUnderlyingEnumValueToEnum(value);
if (Converter != null)
{
diff --git a/src/EFCore/Storage/ValueConversion/ValueConverter`.cs b/src/EFCore/Storage/ValueConversion/ValueConverter`.cs
index 9271af79072..4518aeb645a 100644
--- a/src/EFCore/Storage/ValueConversion/ValueConverter`.cs
+++ b/src/EFCore/Storage/ValueConversion/ValueConverter`.cs
@@ -39,8 +39,8 @@ private static Func SanitizeConverter(Expression v == null
- ? (object)null
- : compiled(Sanitize(v));
+ ? (object)null
+ : compiled(Sanitize(v));
}
private static T Sanitize(object value)
diff --git a/test/EFCore.InMemory.FunctionalTests/Query/GearsOfWarQueryInMemoryTest.cs b/test/EFCore.InMemory.FunctionalTests/Query/GearsOfWarQueryInMemoryTest.cs
index 3494e10dfd2..e763cb39903 100644
--- a/test/EFCore.InMemory.FunctionalTests/Query/GearsOfWarQueryInMemoryTest.cs
+++ b/test/EFCore.InMemory.FunctionalTests/Query/GearsOfWarQueryInMemoryTest.cs
@@ -82,5 +82,11 @@ public override Task Group_by_on_StartsWith_with_null_parameter_as_argument(bool
{
return base.Group_by_on_StartsWith_with_null_parameter_as_argument(async);
}
+
+ [ConditionalTheory(Skip = "issue #18284")]
+ public override Task Enum_closure_typed_as_underlying_type_generates_correct_parameter_type(bool async)
+ {
+ return base.Enum_closure_typed_as_underlying_type_generates_correct_parameter_type(async);
+ }
}
}
diff --git a/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs
index c436c8db3e7..5501db8f5df 100644
--- a/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs
+++ b/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs
@@ -7362,6 +7362,54 @@ public virtual Task Where_TimeSpan_Milliseconds(bool async)
.Where(m => m.Duration.Milliseconds == 1));
}
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Enum_closure_typed_as_underlying_type_generates_correct_parameter_type(bool async)
+ {
+ var prm = (int)AmmunitionType.Cartridge;
+
+ return AssertQuery(
+ async,
+ ss => ss.Set().Where(w => prm == (int)w.AmmunitionType),
+ ss => ss.Set().Where(w => w.AmmunitionType != null && prm == (int)w.AmmunitionType));
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Enum_flags_closure_typed_as_underlying_type_generates_correct_parameter_type(bool async)
+ {
+ var prm = (int)MilitaryRank.Private + (int)MilitaryRank.Sergeant + (int)MilitaryRank.General;
+
+ return AssertQuery(
+ async,
+ ss => ss.Set()
+ .Where(g => (prm & (int)g.Rank) == (int)g.Rank));
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Enum_flags_closure_typed_as_different_type_generates_correct_parameter_type(bool async)
+ {
+ var prm = (byte)MilitaryRank.Private + (byte)MilitaryRank.Sergeant;
+
+ return AssertQuery(
+ async,
+ ss => ss.Set()
+ .Where(g => (prm & (short)g.Rank) == (short)g.Rank));
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Constant_enum_with_same_underlying_value_as_previously_parameterized_int(bool async)
+ {
+ return AssertQueryScalar(
+ async,
+ ss => ss.Set()
+ .OrderBy(g => g.Nickname)
+ .Take(1)
+ .Select(g => g.Rank & MilitaryRank.Private));
+ }
+
protected GearsOfWarContext CreateContext() => Fixture.CreateContext();
protected virtual void ClearLog()
diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs
index 3bab0d87056..c48acc1e441 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs
@@ -7568,6 +7568,51 @@ FROM [Missions] AS [m]
WHERE DATEPART(millisecond, [m].[Duration]) = 1");
}
+ public override async Task Enum_closure_typed_as_underlying_type_generates_correct_parameter_type(bool async)
+ {
+ await base.Enum_closure_typed_as_underlying_type_generates_correct_parameter_type(async);
+
+ AssertSql(
+ @"@__prm_0='1'
+
+SELECT [w].[Id], [w].[AmmunitionType], [w].[IsAutomatic], [w].[Name], [w].[OwnerFullName], [w].[SynergyWithId]
+FROM [Weapons] AS [w]
+WHERE @__prm_0 = [w].[AmmunitionType]");
+ }
+
+ public override async Task Enum_flags_closure_typed_as_underlying_type_generates_correct_parameter_type(bool async)
+ {
+ await base.Enum_flags_closure_typed_as_underlying_type_generates_correct_parameter_type(async);
+
+ AssertSql(
+ @"@__prm_0='133'
+
+SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank]
+FROM [Gears] AS [g]
+WHERE [g].[Discriminator] IN (N'Gear', N'Officer') AND ((@__prm_0 & [g].[Rank]) = [g].[Rank])");
+ }
+
+ public override async Task Enum_flags_closure_typed_as_different_type_generates_correct_parameter_type(bool async)
+ {
+ await base.Enum_flags_closure_typed_as_different_type_generates_correct_parameter_type(async);
+
+ AssertSql(
+ @"");
+ }
+
+ public override async Task Constant_enum_with_same_underlying_value_as_previously_parameterized_int(bool async)
+ {
+ await base.Constant_enum_with_same_underlying_value_as_previously_parameterized_int(async);
+
+ AssertSql(
+ @"@__p_0='1'
+
+SELECT TOP(@__p_0) [g].[Rank] & @__p_0
+FROM [Gears] AS [g]
+WHERE [g].[Discriminator] IN (N'Gear', N'Officer')
+ORDER BY [g].[Nickname]");
+ }
+
private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
}
diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs
index 52e770493d0..5c67c3eb3ff 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs
@@ -7109,6 +7109,62 @@ private class CustomerView19708
#endregion
+ //[ConditionalFact]
+
+ //public void Test19128()
+ //{
+ // var db = new LeadsDbContext();
+ // db.Database.EnsureDeleted();
+ // db.Database.EnsureCreated();
+ // var downloadLead = new Lead { LeadType = LeadType.Download };
+ // var productLead = new Lead { LeadType = LeadType.Product };
+ // var leads = new List { downloadLead, productLead };
+ // db.AddRange(leads);
+ // db.SaveChanges();
+
+ // //fails
+ // var sumLeadTypes = (int)LeadType.Download + (int)LeadType.Product;
+ // var matchingLeads = db.Leads
+ // .Where(l => (sumLeadTypes & (int)l.LeadType) == (int)l.LeadType)
+ // .ToList();
+
+ // // works
+ // //var sumLeadTypes2 = LeadType.Download | LeadType.Product;
+ // //var matchingLeads2 = db.Leads
+ // // .Where(l => (sumLeadTypes2 & l.LeadType) == l.LeadType)
+ // // .ToList();
+ //}
+
+ //public class Lead
+ //{
+ // [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ // public int Id { get; set; }
+ // public LeadType LeadType { get; set; }
+ //}
+
+ //[Flags]
+ //public enum LeadType : short
+ //{
+ // PremiumContent = 1,
+ // ActiveProjectLeads = 2,
+ // Product = 4,
+ // ContactRequest = 8,
+ // Shared = 16,
+ // Download = 32,
+ // //InternalHelpRequest = 64,
+ // //Article = 128
+ //}
+
+ //public class LeadsDbContext : DbContext
+ //{
+ // public DbSet Leads { get; set; }
+
+ // protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
+ // {
+ // optionsBuilder.UseSqlServer(@"Server=.;Database=Repro19128;Trusted_Connection=True;MultipleActiveResultSets=True");
+ // }
+ //}
+
private DbContextOptions _options;
private SqlServerTestStore CreateTestStore(