Skip to content

Commit

Permalink
Merge pull request #42 from yv989c/feature/optimize
Browse files Browse the repository at this point in the history
Feature/optimize
  • Loading branch information
yv989c committed Dec 7, 2023
2 parents fbf58b0 + 262cc6e commit 3e2e205
Show file tree
Hide file tree
Showing 35 changed files with 806 additions and 235 deletions.
198 changes: 93 additions & 105 deletions README.md

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions Version.xml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<Project>
<PropertyGroup>
<VersionEFCore3>3.9.1</VersionEFCore3>
<VersionEFCore5>5.9.1</VersionEFCore5>
<VersionEFCore6>6.9.1</VersionEFCore6>
<VersionEFCore7>7.4.1</VersionEFCore7>
<VersionEFCore8>8.0.0</VersionEFCore8>
<VersionEFCore3>3.9.2</VersionEFCore3>
<VersionEFCore5>5.9.2</VersionEFCore5>
<VersionEFCore6>6.9.2</VersionEFCore6>
<VersionEFCore7>7.4.2</VersionEFCore7>
<VersionEFCore8>8.1.0</VersionEFCore8>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

namespace QueryableValues.SqlServer.Benchmarks;

//[SimpleJob(RunStrategy.Monitoring, warmupCount: 1, iterationCount: 1, invocationCount: 6)]
[SimpleJob(RunStrategy.Monitoring, warmupCount: 1, iterationCount: 25, invocationCount: 200)]
[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)]
[GcServer(true), MemoryDiagnoser]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.4" />
<PackageReference Include="BenchmarkDotNet" Version="0.13.10" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\QueryableValues.SqlServer.EFCore7\QueryableValues.SqlServer.EFCore7.csproj" />
<ProjectReference Include="..\..\src\QueryableValues.SqlServer.EFCore8\QueryableValues.SqlServer.EFCore8.csproj" />
</ItemGroup>

</Project>
7 changes: 3 additions & 4 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@ For a detailed explanation of the problem solved by QueryableValues, please cont

> 💡 Still on Entity Framework 6 (non-core)? Then [QueryableValues `EF6 Edition`](https://github.com/yv989c/BlazarTech.QueryableValues.EF6) is what you need.
## When Should You Use It?
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.

## Your Support is Appreciated!
If you feel that this solution has provided you some value, please consider [buying me a ☕][BuyMeACoffee].

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

### Simple Type Examples

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

Expand Down Expand Up @@ -215,6 +212,8 @@ Please take a look at the [repository][Repository].
[Double]: https://docs.microsoft.com/en-us/dotnet/api/system.double
[DateTime]: https://docs.microsoft.com/en-us/dotnet/api/system.datetime
[DateTimeOffset]: https://docs.microsoft.com/en-us/dotnet/api/system.datetimeoffset
[DateOnly]: https://docs.microsoft.com/en-us/dotnet/api/system.dateonly
[TimeOnly]: https://docs.microsoft.com/en-us/dotnet/api/system.timeonly
[Guid]: https://docs.microsoft.com/en-us/dotnet/api/system.guid
[Char]: https://docs.microsoft.com/en-us/dotnet/api/system.char
[String]: https://docs.microsoft.com/en-us/dotnet/api/system.string
Expand Down
File renamed without changes
File renamed without changes
File renamed without changes
Binary file added docs/benchmarks/images/v8.1.0.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
120 changes: 120 additions & 0 deletions docs/benchmarks/v7.2.0.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="../SharedProjectProperties.xml" />
<Import Project="../../Version.xml" />
<Import Project="../SharedProjectProperties.xml" />
<Import Project="../../Version.xml" />

<PropertyGroup>
<VersionPrefix>$(VersionEFCore3)</VersionPrefix>
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks>
<LangVersion>9.0</LangVersion>
<Configurations>Debug;Release;Test</Configurations>
<DefineConstants>$(DefineConstants);EFCORE;EFCORE3</DefineConstants>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="[3.1.14,5.0)" />
</ItemGroup>
<PropertyGroup>
<VersionPrefix>$(VersionEFCore3)</VersionPrefix>
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks>
<LangVersion>9.0</LangVersion>
<Configurations>Debug;Release;Test</Configurations>
<DefineConstants>$(DefineConstants);EFCORE;EFCORE3</DefineConstants>
</PropertyGroup>

<ItemGroup Condition="'$(TargetFramework)'=='netstandard2.0'">
<PackageReference Include="System.Text.Json" Version="4.7.2" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="6.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="[3.1.14,5.0)" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="../SharedProjectProperties.xml" />
<Import Project="../../Version.xml" />

<PropertyGroup>
<VersionPrefix>$(VersionEFCore5)</VersionPrefix>
<TargetFrameworks>netstandard2.1;net6.0</TargetFrameworks>
<LangVersion>9.0</LangVersion>
<Configurations>Debug;Release;Test</Configurations>
<DefineConstants>$(DefineConstants);EFCORE;EFCORE5</DefineConstants>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="[5.0,6.0)" />
</ItemGroup>
<Import Project="../SharedProjectProperties.xml" />
<Import Project="../../Version.xml" />

<PropertyGroup>
<VersionPrefix>$(VersionEFCore5)</VersionPrefix>
<TargetFrameworks>netstandard2.1;net6.0</TargetFrameworks>
<LangVersion>9.0</LangVersion>
<Configurations>Debug;Release;Test</Configurations>
<DefineConstants>$(DefineConstants);EFCORE;EFCORE5</DefineConstants>
</PropertyGroup>

<ItemGroup Condition="'$(TargetFramework)'=='netstandard2.1'">
<PackageReference Include="System.Text.Json" Version="4.7.2" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="6.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="[5.0,6.0)" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="6.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="[6.0,7.0)" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="../SharedProjectProperties.xml" />
<Import Project="../../Version.xml" />

<PropertyGroup>
<VersionPrefix>$(VersionEFCore7)</VersionPrefix>
<TargetFramework>net6.0</TargetFramework>
<Configurations>Debug;Release;Test</Configurations>
<DefineConstants>$(DefineConstants);EFCORE;EFCORE7</DefineConstants>
</PropertyGroup>
<Import Project="../SharedProjectProperties.xml" />
<Import Project="../../Version.xml" />

<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="[7.0,)" />
</ItemGroup>
<PropertyGroup>
<VersionPrefix>$(VersionEFCore7)</VersionPrefix>
<TargetFramework>net6.0</TargetFramework>
<Configurations>Debug;Release;Test</Configurations>
<DefineConstants>$(DefineConstants);EFCORE;EFCORE7</DefineConstants>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="6.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="[7.0,)" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="../SharedProjectProperties.xml" />
<Import Project="../../Version.xml" />

<PropertyGroup>
<VersionPrefix>$(VersionEFCore8)</VersionPrefix>
<TargetFramework>net8.0</TargetFramework>
<Configurations>Debug;Release;Test</Configurations>
<DefineConstants>$(DefineConstants);EFCORE;EFCORE8</DefineConstants>
</PropertyGroup>
<Import Project="../SharedProjectProperties.xml" />
<Import Project="../../Version.xml" />

<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="[8.0,)" />
</ItemGroup>
<PropertyGroup>
<VersionPrefix>$(VersionEFCore8)</VersionPrefix>
<TargetFramework>net8.0</TargetFramework>
<Configurations>Debug;Release;Test</Configurations>
<DefineConstants>$(DefineConstants);EFCORE;EFCORE8</DefineConstants>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="8.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="[8.0,)" />
</ItemGroup>
</Project>
5 changes: 3 additions & 2 deletions src/QueryableValues.SqlServer/DeferredValues.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@

namespace BlazarTech.QueryableValues
{
internal sealed class DeferredValues<T, T2> : IDeferredValues
internal sealed class DeferredValues<T, T2, TEntity> : IDeferredValues
where T : notnull
where T2 : notnull
where TEntity : QueryableValuesEntity
{
private readonly ISerializer _serializer;
private readonly ValuesWrapper<T, T2> _valuesWrapper;
Expand All @@ -25,7 +26,7 @@ public DeferredValues(ISerializer serializer, ValuesWrapper<T, T2> valuesWrapper
{
_serializer = serializer;
_valuesWrapper = valuesWrapper;
Mappings = EntityPropertyMapping.GetMappings<T2>();
Mappings = EntityPropertyMapping.GetMappings<T2, TEntity>();
}

public string ToString(IFormatProvider? provider) => _serializer.Serialize(_valuesWrapper.ProjectedValues, Mappings);
Expand Down
47 changes: 38 additions & 9 deletions src/QueryableValues.SqlServer/EntityPropertyMapping.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ internal sealed class EntityPropertyMapping
{
internal static readonly IReadOnlyDictionary<Type, EntityPropertyTypeName> SimpleTypes;

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

public PropertyInfo Source { get; }
public PropertyInfo Target { get; }
Expand All @@ -35,7 +35,11 @@ static EntityPropertyMapping()
{ typeof(DateTimeOffset), EntityPropertyTypeName.DateTimeOffset },
{ typeof(Guid), EntityPropertyTypeName.Guid },
{ typeof(char), EntityPropertyTypeName.Char },
{ typeof(string), EntityPropertyTypeName.String }
{ typeof(string), EntityPropertyTypeName.String },
#if EFCORE8
{ typeof(DateOnly), EntityPropertyTypeName.DateOnly },
{ typeof(TimeOnly), EntityPropertyTypeName.TimeOnly }
#endif
};
}

Expand Down Expand Up @@ -87,9 +91,33 @@ public static bool IsSimpleType(Type type)
return SimpleTypes.ContainsKey(normalizedType);
}

public static IReadOnlyList<EntityPropertyMapping> GetMappings(Type sourceType)
private static IReadOnlyList<PropertyInfo> GetTargetTypeProperties(Type targetType)
{
if (MappingCache.TryGetValue(sourceType, out IReadOnlyList<EntityPropertyMapping>? mappingsFromCache))
if (TargetTypePropertyCache.TryGetValue(targetType, out IReadOnlyList<PropertyInfo>? properties))
{
return properties;
}

if (!typeof(QueryableValuesEntity).IsAssignableFrom(targetType))
{
throw new InvalidOperationException();
}

properties = targetType
.GetProperties()
.Where(i => i.Name != QueryableValuesEntity.IndexPropertyName)
.ToArray();

TargetTypePropertyCache.TryAdd(targetType, properties);

return properties;
}

public static IReadOnlyList<EntityPropertyMapping> GetMappings(Type sourceType, Type targetType)
{
var mappingCacheKey = (sourceType, targetType);

if (MappingCache.TryGetValue(mappingCacheKey, out IReadOnlyList<EntityPropertyMapping>? mappingsFromCache))
{
return mappingsFromCache;
}
Expand All @@ -104,7 +132,7 @@ public static IReadOnlyList<EntityPropertyMapping> GetMappings(Type sourceType)
var mappings = new List<EntityPropertyMapping>(sourceProperties.Length);

var targetPropertiesByType = (
from i in EntityProperties
from i in GetTargetTypeProperties(targetType)
group i by GetNormalizedType(i.PropertyType) into g
select g
)
Expand Down Expand Up @@ -136,14 +164,15 @@ select g
}
}

MappingCache.TryAdd(sourceType, mappings);
MappingCache.TryAdd(mappingCacheKey, mappings);

return mappings;
}

public static IReadOnlyList<EntityPropertyMapping> GetMappings<T>()
public static IReadOnlyList<EntityPropertyMapping> GetMappings<TSource, TTargetEntity>()
where TTargetEntity : QueryableValuesEntity
{
return GetMappings(typeof(T));
return GetMappings(typeof(TSource), typeof(TTargetEntity));
}

public object? GetSourceNormalizedValue(object objectInstance)
Expand Down
6 changes: 5 additions & 1 deletion src/QueryableValues.SqlServer/EntityPropertyTypeName.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ internal enum EntityPropertyTypeName
DateTimeOffset,
Guid,
Char,
String
String,
#if EFCORE8
DateOnly,
TimeOnly
#endif
}
}
5 changes: 5 additions & 0 deletions src/QueryableValues.SqlServer/IQueryableFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,10 @@ public IQueryable<TEnum> Create<TEnum>(DbContext dbContext, IEnumerable<TEnum> v
where TEnum : struct, Enum;
IQueryable<TSource> Create<TSource>(DbContext dbContext, IEnumerable<TSource> values, Action<EntityOptionsBuilder<TSource>>? configure)
where TSource : notnull;

#if EFCORE8
IQueryable<DateOnly> Create(DbContext dbContext, IEnumerable<DateOnly> values);
IQueryable<TimeOnly> Create(DbContext dbContext, IEnumerable<TimeOnly> values);
#endif
}
}
Loading

0 comments on commit 3e2e205

Please sign in to comment.