Skip to content

Commit 114725a

Browse files
authored
Merge pull request #35 from yv989c/feature/enum-support
Feature/enum support
2 parents 2b487a6 + cdde4bc commit 114725a

16 files changed

+421
-36
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ Below are a few examples composing a query using the values provided by an [IEnu
9292

9393
### Simple Type Examples
9494

95-
> 💡 Supports [Byte], [Int16], [Int32], [Int64], [Decimal], [Single], [Double], [DateTime], [DateTimeOffset], [Guid], [Char], and [String].
95+
> 💡 Supports [Byte], [Int16], [Int32], [Int64], [Decimal], [Single], [Double], [DateTime], [DateTimeOffset], [Guid], [Char], [String], and [Enum].
9696
9797
Using the [Contains][ContainsQueryable] LINQ method:
9898

@@ -474,6 +474,7 @@ PRs are welcome! 🙂
474474
[Guid]: https://docs.microsoft.com/en-us/dotnet/api/system.guid
475475
[Char]: https://docs.microsoft.com/en-us/dotnet/api/system.char
476476
[String]: https://docs.microsoft.com/en-us/dotnet/api/system.string
477+
[Enum]: https://docs.microsoft.com/en-us/dotnet/api/system.enum
477478
[BuyMeACoffee]: https://www.buymeacoffee.com/yv989c
478479
[BuyMeACoffeeButton]: /docs/images/bmc-48.svg
479480
[Repository]: https://github.com/yv989c/BlazarTech.QueryableValues

Version.xml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<Project>
22
<PropertyGroup>
3-
<VersionEFCore3>3.8.0</VersionEFCore3>
4-
<VersionEFCore5>5.8.0</VersionEFCore5>
5-
<VersionEFCore6>6.8.0</VersionEFCore6>
6-
<VersionEFCore7>7.3.0</VersionEFCore7>
3+
<VersionEFCore3>3.9.0</VersionEFCore3>
4+
<VersionEFCore5>5.9.0</VersionEFCore5>
5+
<VersionEFCore6>6.9.0</VersionEFCore6>
6+
<VersionEFCore7>7.4.0</VersionEFCore7>
77
</PropertyGroup>
88
</Project>

docs/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ Below are a few examples composing a query using the values provided by an [IEnu
8888

8989
### Simple Type Examples
9090

91-
> 💡 Supports [Byte], [Int16], [Int32], [Int64], [Decimal], [Single], [Double], [DateTime], [DateTimeOffset], [Guid], [Char], and [String].
91+
> 💡 Supports [Byte], [Int16], [Int32], [Int64], [Decimal], [Single], [Double], [DateTime], [DateTimeOffset], [Guid], [Char], [String], and [Enum].
9292
9393
Using the [Contains][ContainsQueryable] LINQ method:
9494

@@ -218,6 +218,7 @@ Please take a look at the [repository][Repository].
218218
[Guid]: https://docs.microsoft.com/en-us/dotnet/api/system.guid
219219
[Char]: https://docs.microsoft.com/en-us/dotnet/api/system.char
220220
[String]: https://docs.microsoft.com/en-us/dotnet/api/system.string
221+
[Enum]: https://docs.microsoft.com/en-us/dotnet/api/system.enum
221222
[BuyMeACoffee]: https://www.buymeacoffee.com/yv989c
222223
[BuyMeACoffeeButton]: https://raw.githubusercontent.com/yv989c/BlazarTech.QueryableValues/develop/docs/images/bmc-48.svg
223224
[Repository]: https://github.com/yv989c/BlazarTech.QueryableValues

src/QueryableValues.SqlServer/EntityPropertyMapping.cs

Lines changed: 67 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ internal sealed class EntityPropertyMapping
1717
public PropertyInfo Target { get; }
1818
public Type NormalizedType { get; }
1919
public EntityPropertyTypeName TypeName { get; }
20+
public bool IsSourceEnum { get; }
2021

2122
static EntityPropertyMapping()
2223
{
@@ -38,23 +39,47 @@ static EntityPropertyMapping()
3839
};
3940
}
4041

41-
private EntityPropertyMapping(PropertyInfo source, PropertyInfo target, Type normalizedType)
42+
private EntityPropertyMapping(PropertyInfo source, PropertyInfo target, Type normalizedType, bool isSourceEnum)
4243
{
4344
Source = source;
4445
Target = target;
4546
NormalizedType = normalizedType;
47+
TypeName = GetTypeName(normalizedType);
48+
IsSourceEnum = isSourceEnum;
4649

47-
if (SimpleTypes.TryGetValue(normalizedType, out EntityPropertyTypeName typeName))
50+
if (TypeName == EntityPropertyTypeName.Unknown)
4851
{
49-
TypeName = typeName;
52+
throw new NotSupportedException($"{source.PropertyType.FullName} is not supported.");
53+
}
54+
}
55+
56+
public static EntityPropertyTypeName GetTypeName(Type type)
57+
{
58+
if (SimpleTypes.TryGetValue(type, out EntityPropertyTypeName typeName))
59+
{
60+
return typeName;
5061
}
5162
else
5263
{
53-
throw new NotSupportedException($"{source.PropertyType.FullName} is not supported.");
64+
return EntityPropertyTypeName.Unknown;
5465
}
5566
}
5667

57-
private static Type GetNormalizedType(Type type) => Nullable.GetUnderlyingType(type) ?? type;
68+
public static Type GetNormalizedType(Type type, out bool isEnum)
69+
{
70+
type = Nullable.GetUnderlyingType(type) ?? type;
71+
72+
isEnum = type.IsEnum;
73+
74+
if (isEnum)
75+
{
76+
type = Enum.GetUnderlyingType(type);
77+
}
78+
79+
return type;
80+
}
81+
82+
public static Type GetNormalizedType(Type type) => GetNormalizedType(type, out _);
5883

5984
public static bool IsSimpleType(Type type)
6085
{
@@ -87,9 +112,9 @@ select g
87112

88113
foreach (var sourceProperty in sourceProperties)
89114
{
90-
var propertyType = GetNormalizedType(sourceProperty.PropertyType);
115+
var sourcePropertyNormalizedType = GetNormalizedType(sourceProperty.PropertyType, out var isSourceEnum);
91116

92-
if (targetPropertiesByType.TryGetValue(propertyType, out Queue<PropertyInfo>? targetProperties))
117+
if (targetPropertiesByType.TryGetValue(sourcePropertyNormalizedType, out Queue<PropertyInfo>? targetProperties))
93118
{
94119
if (targetProperties.Count == 0)
95120
{
@@ -99,7 +124,8 @@ select g
99124
var mapping = new EntityPropertyMapping(
100125
sourceProperty,
101126
targetProperties.Dequeue(),
102-
propertyType
127+
sourcePropertyNormalizedType,
128+
isSourceEnum
103129
);
104130

105131
mappings.Add(mapping);
@@ -119,5 +145,38 @@ public static IReadOnlyList<EntityPropertyMapping> GetMappings<T>()
119145
{
120146
return GetMappings(typeof(T));
121147
}
148+
149+
public object? GetSourceNormalizedValue(object objectInstance)
150+
{
151+
var value = Source.GetValue(objectInstance);
152+
153+
if (value is null)
154+
{
155+
return null;
156+
}
157+
158+
if (IsSourceEnum)
159+
{
160+
switch (TypeName)
161+
{
162+
case EntityPropertyTypeName.Int32:
163+
value = (int)value;
164+
break;
165+
case EntityPropertyTypeName.Byte:
166+
value = (byte)value;
167+
break;
168+
case EntityPropertyTypeName.Int16:
169+
value = (short)value;
170+
break;
171+
case EntityPropertyTypeName.Int64:
172+
value = (long)value;
173+
break;
174+
default:
175+
throw new NotSupportedException($"The underlying type of {NormalizedType.FullName} ({Enum.GetUnderlyingType(NormalizedType).FullName}) is not supported.");
176+
}
177+
}
178+
179+
return value;
180+
}
122181
}
123182
}

src/QueryableValues.SqlServer/EntityPropertyTypeName.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
{
33
internal enum EntityPropertyTypeName
44
{
5+
Unknown,
56
Boolean,
67
Byte,
78
Int16,

src/QueryableValues.SqlServer/IQueryableFactory.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
#if EFCORE
2-
using BlazarTech.QueryableValues.Builders;
1+
using BlazarTech.QueryableValues.Builders;
32
using Microsoft.EntityFrameworkCore;
43
using System;
54
using System.Collections.Generic;
@@ -21,8 +20,9 @@ internal interface IQueryableFactory
2120
IQueryable<char> Create(DbContext dbContext, IEnumerable<char> values, bool isUnicode);
2221
IQueryable<string> Create(DbContext dbContext, IEnumerable<string> values, bool isUnicode);
2322
IQueryable<Guid> Create(DbContext dbContext, IEnumerable<Guid> values);
23+
public IQueryable<TEnum> Create<TEnum>(DbContext dbContext, IEnumerable<TEnum> values)
24+
where TEnum : struct, Enum;
2425
IQueryable<TSource> Create<TSource>(DbContext dbContext, IEnumerable<TSource> values, Action<EntityOptionsBuilder<TSource>>? configure)
2526
where TSource : notnull;
2627
}
2728
}
28-
#endif

src/QueryableValues.SqlServer/QueryableValues.SqlServer.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44
<TargetFramework>netstandard2.1</TargetFramework>
55
<IsPackable>false</IsPackable>
66
<Configurations>Debug;Release;Test</Configurations>
7-
</PropertyGroup>
7+
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
8+
</PropertyGroup>
89
</Project>

src/QueryableValues.SqlServer/QueryableValuesDbContextExtensions.cs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
#if EFCORE
2-
using BlazarTech.QueryableValues.Builders;
1+
using BlazarTech.QueryableValues.Builders;
32
using BlazarTech.QueryableValues.SqlServer;
43
using Microsoft.EntityFrameworkCore;
54
using Microsoft.EntityFrameworkCore.Infrastructure;
@@ -227,6 +226,27 @@ public static IQueryable<Guid> AsQueryableValues(this DbContext dbContext, IEnum
227226
return GetQueryableFactory(dbContext).Create(dbContext, values);
228227
}
229228

229+
/// <summary>
230+
/// Allows an <see cref="IEnumerable{Enum}">IEnumerable&lt;Enum&gt;</see> to be composed in an Entity Framework query.
231+
/// </summary>
232+
/// <remarks>
233+
/// The supported underlying types are: <see cref="byte"/>, <see cref="short"/>, <see cref="int"/>, and <see cref="long"/>.
234+
/// <para>
235+
/// More info: <see href="https://learn.microsoft.com/en-us/dotnet/api/system.enum#remarks"/>.
236+
/// </para>
237+
/// </remarks>
238+
/// <param name="dbContext">The <see cref="DbContext"/> owning the query.</param>
239+
/// <param name="values">The sequence of values to compose.</param>
240+
/// <returns>An <see cref="IQueryable{Enum}">IQueryable&lt;Enum&gt;</see> that can be composed with other entities in the query.</returns>
241+
/// <exception cref="ArgumentNullException"></exception>
242+
/// <exception cref="InvalidOperationException"></exception>
243+
public static IQueryable<TEnum> AsQueryableValues<TEnum>(this DbContext dbContext, IEnumerable<TEnum> values)
244+
where TEnum : struct, Enum
245+
{
246+
ValidateParameters(dbContext, values);
247+
return GetQueryableFactory(dbContext).Create(dbContext, values);
248+
}
249+
230250
/// <summary>
231251
/// Allows an <see cref="IEnumerable{T}"/> to be composed in an Entity Framework query.
232252
/// </summary>
@@ -244,4 +264,3 @@ public static IQueryable<TSource> AsQueryableValues<TSource>(this DbContext dbCo
244264
}
245265
}
246266
}
247-
#endif

src/QueryableValues.SqlServer/QueryableValuesEnabledDbContextExtensions.cs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
#if EFCORE
2-
using BlazarTech.QueryableValues.Builders;
1+
using BlazarTech.QueryableValues.Builders;
32
using Microsoft.EntityFrameworkCore;
43
using System;
54
using System.Collections.Generic;
@@ -200,6 +199,21 @@ public static IQueryable<Guid> AsQueryableValues(this IQueryableValuesEnabledDbC
200199
return GetDbContext(dbContext).AsQueryableValues(values);
201200
}
202201

202+
/// <summary>
203+
/// <inheritdoc cref="QueryableValuesDbContextExtensions.AsQueryableValues{TEnum}(DbContext, IEnumerable{TEnum})"/>
204+
/// </summary>
205+
/// <param name="dbContext"><inheritdoc cref="QueryableValuesDbContextExtensions.AsQueryableValues{TEnum}(DbContext, IEnumerable{TEnum})" path="/param[@name='dbContext']"/></param>
206+
/// <param name="values"><inheritdoc cref="QueryableValuesDbContextExtensions.AsQueryableValues{TEnum}(DbContext, IEnumerable{TEnum})" path="/param[@name='values']"/></param>
207+
/// <returns><inheritdoc cref="QueryableValuesDbContextExtensions.AsQueryableValues{TEnum}(DbContext, IEnumerable{TEnum})"/></returns>
208+
/// <remarks><inheritdoc cref="QueryableValuesDbContextExtensions.AsQueryableValues{TEnum}(DbContext, IEnumerable{TEnum})"/></remarks>
209+
/// <exception cref="ArgumentNullException"></exception>
210+
/// <exception cref="InvalidOperationException"></exception>
211+
public static IQueryable<TEnum> AsQueryableValues<TEnum>(this IQueryableValuesEnabledDbContext dbContext, IEnumerable<TEnum> values)
212+
where TEnum : struct, Enum
213+
{
214+
return GetDbContext(dbContext).AsQueryableValues(values);
215+
}
216+
203217
/// <summary>
204218
/// <inheritdoc cref="QueryableValuesDbContextExtensions.AsQueryableValues{TSource}(DbContext, IEnumerable{TSource}, Action{EntityOptionsBuilder{TSource}}?)"/>
205219
/// </summary>
@@ -217,4 +231,3 @@ public static IQueryable<TSource> AsQueryableValues<TSource>(this IQueryableValu
217231
}
218232
}
219233
}
220-
#endif

src/QueryableValues.SqlServer/Serializers/JsonSerializer.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,13 @@ public void WriteValue(Utf8JsonWriter writer, object entity)
166166
{
167167
if (_writeValue != null)
168168
{
169-
var value = Mapping.Source.GetValue(entity);
169+
var value = Mapping.GetSourceNormalizedValue(entity);
170+
171+
if (value is null)
172+
{
173+
return;
174+
}
175+
170176
_writeValue.Invoke(writer, value);
171177
}
172178
}

0 commit comments

Comments
 (0)