Skip to content

Commit 3e2e205

Browse files
authored
Merge pull request #42 from yv989c/feature/optimize
Feature/optimize
2 parents fbf58b0 + 262cc6e commit 3e2e205

35 files changed

+806
-235
lines changed

README.md

Lines changed: 93 additions & 105 deletions
Large diffs are not rendered by default.

Version.xml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<Project>
22
<PropertyGroup>
3-
<VersionEFCore3>3.9.1</VersionEFCore3>
4-
<VersionEFCore5>5.9.1</VersionEFCore5>
5-
<VersionEFCore6>6.9.1</VersionEFCore6>
6-
<VersionEFCore7>7.4.1</VersionEFCore7>
7-
<VersionEFCore8>8.0.0</VersionEFCore8>
3+
<VersionEFCore3>3.9.2</VersionEFCore3>
4+
<VersionEFCore5>5.9.2</VersionEFCore5>
5+
<VersionEFCore6>6.9.2</VersionEFCore6>
6+
<VersionEFCore7>7.4.2</VersionEFCore7>
7+
<VersionEFCore8>8.1.0</VersionEFCore8>
88
</PropertyGroup>
99
</Project>

benchmarks/QueryableValues.SqlServer.Benchmarks/ContainsBenchmarks.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
namespace QueryableValues.SqlServer.Benchmarks;
1010

11+
//[SimpleJob(RunStrategy.Monitoring, warmupCount: 1, iterationCount: 1, invocationCount: 6)]
1112
[SimpleJob(RunStrategy.Monitoring, warmupCount: 1, iterationCount: 25, invocationCount: 200)]
1213
[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)]
1314
[GcServer(true), MemoryDiagnoser]

benchmarks/QueryableValues.SqlServer.Benchmarks/QueryableValues.SqlServer.Benchmarks.csproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,18 @@
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFramework>net6.0</TargetFramework>
5+
<TargetFramework>net8.0</TargetFramework>
66
<ImplicitUsings>enable</ImplicitUsings>
77
<Nullable>enable</Nullable>
88
</PropertyGroup>
99

1010
<ItemGroup>
11-
<PackageReference Include="BenchmarkDotNet" Version="0.13.5" />
12-
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.4" />
11+
<PackageReference Include="BenchmarkDotNet" Version="0.13.10" />
12+
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.0" />
1313
</ItemGroup>
1414

1515
<ItemGroup>
16-
<ProjectReference Include="..\..\src\QueryableValues.SqlServer.EFCore7\QueryableValues.SqlServer.EFCore7.csproj" />
16+
<ProjectReference Include="..\..\src\QueryableValues.SqlServer.EFCore8\QueryableValues.SqlServer.EFCore8.csproj" />
1717
</ItemGroup>
1818

1919
</Project>

docs/README.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@ For a detailed explanation of the problem solved by QueryableValues, please cont
1818

1919
> 💡 Still on Entity Framework 6 (non-core)? Then [QueryableValues `EF6 Edition`](https://github.com/yv989c/BlazarTech.QueryableValues.EF6) is what you need.
2020
21-
## When Should You Use It?
22-
The `AsQueryableValues` extension method is intended for queries that are dependent upon a *non-constant* sequence of external values. It provides a solution to the following [EF Core issue](https://github.com/dotnet/efcore/issues/13617) and enables other currently unsupported scenarios; like the ability to efficiently create joins with in-memory data.
23-
2421
## Your Support is Appreciated!
2522
If you feel that this solution has provided you some value, please consider [buying me a ☕][BuyMeACoffee].
2623

@@ -88,7 +85,7 @@ Below are a few examples composing a query using the values provided by an [IEnu
8885

8986
### Simple Type Examples
9087

91-
> 💡 Supports [Byte], [Int16], [Int32], [Int64], [Decimal], [Single], [Double], [DateTime], [DateTimeOffset], [Guid], [Char], [String], and [Enum].
88+
> 💡 Supports [Byte], [Int16], [Int32], [Int64], [Decimal], [Single], [Double], [DateTime], [DateTimeOffset], [DateOnly], [TimeOnly], [Guid], [Char], [String], and [Enum].
9289
9390
Using the [Contains][ContainsQueryable] LINQ method:
9491

@@ -215,6 +212,8 @@ Please take a look at the [repository][Repository].
215212
[Double]: https://docs.microsoft.com/en-us/dotnet/api/system.double
216213
[DateTime]: https://docs.microsoft.com/en-us/dotnet/api/system.datetime
217214
[DateTimeOffset]: https://docs.microsoft.com/en-us/dotnet/api/system.datetimeoffset
215+
[DateOnly]: https://docs.microsoft.com/en-us/dotnet/api/system.dateonly
216+
[TimeOnly]: https://docs.microsoft.com/en-us/dotnet/api/system.timeonly
218217
[Guid]: https://docs.microsoft.com/en-us/dotnet/api/system.guid
219218
[Char]: https://docs.microsoft.com/en-us/dotnet/api/system.char
220219
[String]: https://docs.microsoft.com/en-us/dotnet/api/system.string
File renamed without changes.

docs/benchmarks/images/v8.1.0.png

210 KB
Loading

docs/benchmarks/v7.2.0.md

Lines changed: 120 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
11
<Project Sdk="Microsoft.NET.Sdk">
2-
<Import Project="../SharedProjectProperties.xml" />
3-
<Import Project="../../Version.xml" />
2+
<Import Project="../SharedProjectProperties.xml" />
3+
<Import Project="../../Version.xml" />
44

5-
<PropertyGroup>
6-
<VersionPrefix>$(VersionEFCore3)</VersionPrefix>
7-
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks>
8-
<LangVersion>9.0</LangVersion>
9-
<Configurations>Debug;Release;Test</Configurations>
10-
<DefineConstants>$(DefineConstants);EFCORE;EFCORE3</DefineConstants>
11-
</PropertyGroup>
12-
13-
<ItemGroup>
14-
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="[3.1.14,5.0)" />
15-
</ItemGroup>
5+
<PropertyGroup>
6+
<VersionPrefix>$(VersionEFCore3)</VersionPrefix>
7+
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks>
8+
<LangVersion>9.0</LangVersion>
9+
<Configurations>Debug;Release;Test</Configurations>
10+
<DefineConstants>$(DefineConstants);EFCORE;EFCORE3</DefineConstants>
11+
</PropertyGroup>
12+
13+
<ItemGroup Condition="'$(TargetFramework)'=='netstandard2.0'">
14+
<PackageReference Include="System.Text.Json" Version="4.7.2" />
15+
</ItemGroup>
16+
17+
<ItemGroup>
18+
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="6.0.8" />
19+
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="[3.1.14,5.0)" />
20+
</ItemGroup>
1621
</Project>
Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
11
<Project Sdk="Microsoft.NET.Sdk">
2-
<Import Project="../SharedProjectProperties.xml" />
3-
<Import Project="../../Version.xml" />
4-
5-
<PropertyGroup>
6-
<VersionPrefix>$(VersionEFCore5)</VersionPrefix>
7-
<TargetFrameworks>netstandard2.1;net6.0</TargetFrameworks>
8-
<LangVersion>9.0</LangVersion>
9-
<Configurations>Debug;Release;Test</Configurations>
10-
<DefineConstants>$(DefineConstants);EFCORE;EFCORE5</DefineConstants>
11-
</PropertyGroup>
12-
13-
<ItemGroup>
14-
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="[5.0,6.0)" />
15-
</ItemGroup>
2+
<Import Project="../SharedProjectProperties.xml" />
3+
<Import Project="../../Version.xml" />
4+
5+
<PropertyGroup>
6+
<VersionPrefix>$(VersionEFCore5)</VersionPrefix>
7+
<TargetFrameworks>netstandard2.1;net6.0</TargetFrameworks>
8+
<LangVersion>9.0</LangVersion>
9+
<Configurations>Debug;Release;Test</Configurations>
10+
<DefineConstants>$(DefineConstants);EFCORE;EFCORE5</DefineConstants>
11+
</PropertyGroup>
12+
13+
<ItemGroup Condition="'$(TargetFramework)'=='netstandard2.1'">
14+
<PackageReference Include="System.Text.Json" Version="4.7.2" />
15+
</ItemGroup>
16+
17+
<ItemGroup>
18+
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="6.0.8" />
19+
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="[5.0,6.0)" />
20+
</ItemGroup>
1621
</Project>

src/QueryableValues.SqlServer.EFCore6/QueryableValues.SqlServer.EFCore6.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
</PropertyGroup>
1111

1212
<ItemGroup>
13+
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="6.0.8" />
1314
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="[6.0,7.0)" />
1415
</ItemGroup>
1516
</Project>
Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
<Project Sdk="Microsoft.NET.Sdk">
2-
<Import Project="../SharedProjectProperties.xml" />
3-
<Import Project="../../Version.xml" />
4-
5-
<PropertyGroup>
6-
<VersionPrefix>$(VersionEFCore7)</VersionPrefix>
7-
<TargetFramework>net6.0</TargetFramework>
8-
<Configurations>Debug;Release;Test</Configurations>
9-
<DefineConstants>$(DefineConstants);EFCORE;EFCORE7</DefineConstants>
10-
</PropertyGroup>
2+
<Import Project="../SharedProjectProperties.xml" />
3+
<Import Project="../../Version.xml" />
114

12-
<ItemGroup>
13-
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="[7.0,)" />
14-
</ItemGroup>
5+
<PropertyGroup>
6+
<VersionPrefix>$(VersionEFCore7)</VersionPrefix>
7+
<TargetFramework>net6.0</TargetFramework>
8+
<Configurations>Debug;Release;Test</Configurations>
9+
<DefineConstants>$(DefineConstants);EFCORE;EFCORE7</DefineConstants>
10+
</PropertyGroup>
11+
12+
<ItemGroup>
13+
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="6.0.8" />
14+
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="[7.0,)" />
15+
</ItemGroup>
1516
</Project>
Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
<Project Sdk="Microsoft.NET.Sdk">
2-
<Import Project="../SharedProjectProperties.xml" />
3-
<Import Project="../../Version.xml" />
4-
5-
<PropertyGroup>
6-
<VersionPrefix>$(VersionEFCore8)</VersionPrefix>
7-
<TargetFramework>net8.0</TargetFramework>
8-
<Configurations>Debug;Release;Test</Configurations>
9-
<DefineConstants>$(DefineConstants);EFCORE;EFCORE8</DefineConstants>
10-
</PropertyGroup>
2+
<Import Project="../SharedProjectProperties.xml" />
3+
<Import Project="../../Version.xml" />
114

12-
<ItemGroup>
13-
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="[8.0,)" />
14-
</ItemGroup>
5+
<PropertyGroup>
6+
<VersionPrefix>$(VersionEFCore8)</VersionPrefix>
7+
<TargetFramework>net8.0</TargetFramework>
8+
<Configurations>Debug;Release;Test</Configurations>
9+
<DefineConstants>$(DefineConstants);EFCORE;EFCORE8</DefineConstants>
10+
</PropertyGroup>
11+
12+
<ItemGroup>
13+
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="8.0.0" />
14+
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="[8.0,)" />
15+
</ItemGroup>
1516
</Project>

src/QueryableValues.SqlServer/DeferredValues.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44

55
namespace BlazarTech.QueryableValues
66
{
7-
internal sealed class DeferredValues<T, T2> : IDeferredValues
7+
internal sealed class DeferredValues<T, T2, TEntity> : IDeferredValues
88
where T : notnull
99
where T2 : notnull
10+
where TEntity : QueryableValuesEntity
1011
{
1112
private readonly ISerializer _serializer;
1213
private readonly ValuesWrapper<T, T2> _valuesWrapper;
@@ -25,7 +26,7 @@ public DeferredValues(ISerializer serializer, ValuesWrapper<T, T2> valuesWrapper
2526
{
2627
_serializer = serializer;
2728
_valuesWrapper = valuesWrapper;
28-
Mappings = EntityPropertyMapping.GetMappings<T2>();
29+
Mappings = EntityPropertyMapping.GetMappings<T2, TEntity>();
2930
}
3031

3132
public string ToString(IFormatProvider? provider) => _serializer.Serialize(_valuesWrapper.ProjectedValues, Mappings);

src/QueryableValues.SqlServer/EntityPropertyMapping.cs

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ internal sealed class EntityPropertyMapping
1010
{
1111
internal static readonly IReadOnlyDictionary<Type, EntityPropertyTypeName> SimpleTypes;
1212

13-
private static readonly PropertyInfo[] EntityProperties = typeof(QueryableValuesEntity).GetProperties().Where(i => i.Name != QueryableValuesEntity.IndexPropertyName).ToArray();
14-
private static readonly ConcurrentDictionary<Type, IReadOnlyList<EntityPropertyMapping>> MappingCache = new ConcurrentDictionary<Type, IReadOnlyList<EntityPropertyMapping>>();
13+
private static readonly ConcurrentDictionary<Type, IReadOnlyList<PropertyInfo>> TargetTypePropertyCache = new ConcurrentDictionary<Type, IReadOnlyList<PropertyInfo>>();
14+
private static readonly ConcurrentDictionary<(Type, Type), IReadOnlyList<EntityPropertyMapping>> MappingCache = new ConcurrentDictionary<(Type, Type), IReadOnlyList<EntityPropertyMapping>>();
1515

1616
public PropertyInfo Source { get; }
1717
public PropertyInfo Target { get; }
@@ -35,7 +35,11 @@ static EntityPropertyMapping()
3535
{ typeof(DateTimeOffset), EntityPropertyTypeName.DateTimeOffset },
3636
{ typeof(Guid), EntityPropertyTypeName.Guid },
3737
{ typeof(char), EntityPropertyTypeName.Char },
38-
{ typeof(string), EntityPropertyTypeName.String }
38+
{ typeof(string), EntityPropertyTypeName.String },
39+
#if EFCORE8
40+
{ typeof(DateOnly), EntityPropertyTypeName.DateOnly },
41+
{ typeof(TimeOnly), EntityPropertyTypeName.TimeOnly }
42+
#endif
3943
};
4044
}
4145

@@ -87,9 +91,33 @@ public static bool IsSimpleType(Type type)
8791
return SimpleTypes.ContainsKey(normalizedType);
8892
}
8993

90-
public static IReadOnlyList<EntityPropertyMapping> GetMappings(Type sourceType)
94+
private static IReadOnlyList<PropertyInfo> GetTargetTypeProperties(Type targetType)
9195
{
92-
if (MappingCache.TryGetValue(sourceType, out IReadOnlyList<EntityPropertyMapping>? mappingsFromCache))
96+
if (TargetTypePropertyCache.TryGetValue(targetType, out IReadOnlyList<PropertyInfo>? properties))
97+
{
98+
return properties;
99+
}
100+
101+
if (!typeof(QueryableValuesEntity).IsAssignableFrom(targetType))
102+
{
103+
throw new InvalidOperationException();
104+
}
105+
106+
properties = targetType
107+
.GetProperties()
108+
.Where(i => i.Name != QueryableValuesEntity.IndexPropertyName)
109+
.ToArray();
110+
111+
TargetTypePropertyCache.TryAdd(targetType, properties);
112+
113+
return properties;
114+
}
115+
116+
public static IReadOnlyList<EntityPropertyMapping> GetMappings(Type sourceType, Type targetType)
117+
{
118+
var mappingCacheKey = (sourceType, targetType);
119+
120+
if (MappingCache.TryGetValue(mappingCacheKey, out IReadOnlyList<EntityPropertyMapping>? mappingsFromCache))
93121
{
94122
return mappingsFromCache;
95123
}
@@ -104,7 +132,7 @@ public static IReadOnlyList<EntityPropertyMapping> GetMappings(Type sourceType)
104132
var mappings = new List<EntityPropertyMapping>(sourceProperties.Length);
105133

106134
var targetPropertiesByType = (
107-
from i in EntityProperties
135+
from i in GetTargetTypeProperties(targetType)
108136
group i by GetNormalizedType(i.PropertyType) into g
109137
select g
110138
)
@@ -136,14 +164,15 @@ select g
136164
}
137165
}
138166

139-
MappingCache.TryAdd(sourceType, mappings);
167+
MappingCache.TryAdd(mappingCacheKey, mappings);
140168

141169
return mappings;
142170
}
143171

144-
public static IReadOnlyList<EntityPropertyMapping> GetMappings<T>()
172+
public static IReadOnlyList<EntityPropertyMapping> GetMappings<TSource, TTargetEntity>()
173+
where TTargetEntity : QueryableValuesEntity
145174
{
146-
return GetMappings(typeof(T));
175+
return GetMappings(typeof(TSource), typeof(TTargetEntity));
147176
}
148177

149178
public object? GetSourceNormalizedValue(object objectInstance)

src/QueryableValues.SqlServer/EntityPropertyTypeName.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ internal enum EntityPropertyTypeName
1515
DateTimeOffset,
1616
Guid,
1717
Char,
18-
String
18+
String,
19+
#if EFCORE8
20+
DateOnly,
21+
TimeOnly
22+
#endif
1923
}
2024
}

src/QueryableValues.SqlServer/IQueryableFactory.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,10 @@ public IQueryable<TEnum> Create<TEnum>(DbContext dbContext, IEnumerable<TEnum> v
2424
where TEnum : struct, Enum;
2525
IQueryable<TSource> Create<TSource>(DbContext dbContext, IEnumerable<TSource> values, Action<EntityOptionsBuilder<TSource>>? configure)
2626
where TSource : notnull;
27+
28+
#if EFCORE8
29+
IQueryable<DateOnly> Create(DbContext dbContext, IEnumerable<DateOnly> values);
30+
IQueryable<TimeOnly> Create(DbContext dbContext, IEnumerable<TimeOnly> values);
31+
#endif
2732
}
2833
}

0 commit comments

Comments
 (0)