diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props
index 5003ca7..dc0bc13 100644
--- a/src/Directory.Packages.props
+++ b/src/Directory.Packages.props
@@ -3,8 +3,8 @@
true
-
-
+
+
diff --git a/src/GenericQueryable.EntityFramework/EfFetchService.cs b/src/GenericQueryable.EntityFramework/EfFetchService.cs
index 3787578..cbf435e 100644
--- a/src/GenericQueryable.EntityFramework/EfFetchService.cs
+++ b/src/GenericQueryable.EntityFramework/EfFetchService.cs
@@ -1,9 +1,8 @@
-using System.Collections.Concurrent;
-using System.Linq.Expressions;
+using System.Linq.Expressions;
using System.Reflection;
using CommonFramework;
-using CommonFramework.ExpressionEvaluate;
+
using GenericQueryable.Fetching;
using Microsoft.EntityFrameworkCore;
@@ -12,67 +11,28 @@
namespace GenericQueryable.EntityFramework;
-public class EfFetchService([FromKeyedServices(RootFetchRuleExpander.Key)] IFetchRuleExpander fetchRuleExpander) : IFetchService
+public class EfFetchService([FromKeyedServices(RootFetchRuleExpander.Key)] IFetchRuleExpander fetchRuleExpander) : FetchService(fetchRuleExpander)
{
- private readonly ConcurrentDictionary> rootCache = [];
-
- public virtual IQueryable ApplyFetch(IQueryable source, FetchRule fetchRule)
+ public override IQueryable ApplyFetch(IQueryable source, FetchRule fetchRule)
where TSource : class
{
- return fetchRule switch
+ if (fetchRule is UntypedFetchRule untypedFetchRule)
{
- UntypedFetchRule untypedFetchRule => source.Include(untypedFetchRule.Path),
-
- _ => this.GetApplyFetchFunc(fetchRule).Invoke(source)
- };
- }
-
- private Func, IQueryable> GetApplyFetchFunc(FetchRule fetchRule)
- where TSource : class
- {
- return this.rootCache
- .GetOrAdd(typeof(TSource), _ => new ConcurrentDictionary())
- .GetOrAdd(fetchRule.GetType(), _ => new ConcurrentDictionary, Func, IQueryable>>())
- .Pipe(v => (ConcurrentDictionary, Func, IQueryable>>)v)
- .GetOrAdd(fetchRule, _ =>
- {
- var fetchExpr = this.GetApplyFetchExpression(fetchRuleExpander.Expand(fetchRule));
-
- return fetchExpr.Compile();
- });
- }
-
- private Expression, IQueryable>> GetApplyFetchExpression(PropertyFetchRule fetchRule)
- where TSource : class
- {
- var startState = ExpressionHelper.GetIdentity>();
-
- return fetchRule.Paths.Aggregate(startState, (state, path) =>
+ return source.Include(untypedFetchRule.Path);
+ }
+ else
{
- var nextApplyFunc = GetApplyFetchExpression(path);
-
- return ExpressionEvaluateHelper.InlineEvaluate, IQueryable>>(ee =>
-
- q => ee.Evaluate(nextApplyFunc, ee.Evaluate(state, q)));
- });
+ return base.ApplyFetch(source, fetchRule);
+ }
}
- private static Expression, IQueryable>> GetApplyFetchExpression(LambdaExpressionPath fetchPath)
+ protected override IEnumerable GetFetchMethods(LambdaExpressionPath fetchPath)
where TSource : class
{
- LambdaExpression startState = ExpressionHelper.GetIdentity>();
-
- var resultBody = fetchPath
+ return fetchPath
.Properties
.ZipStrong(new LambdaExpression?[] { null }.Concat(fetchPath.Properties.SkipLast(1)), (prop, prevProp) => new { prop, prevProp })
- .Aggregate(startState.Body, (state, pair) =>
- {
- var fetchMethod = GetFetchMethod(pair.prop, pair.prevProp);
-
- return Expression.Call(fetchMethod, state, pair.prop);
- });
-
- return Expression.Lambda, IQueryable>>(resultBody, startState.Parameters);
+ .Select(pair => GetFetchMethod(pair.prop, pair.prevProp));
}
private static MethodInfo GetFetchMethod(LambdaExpression prop, LambdaExpression? prevProp)
diff --git a/src/GenericQueryable.Runtime/Fetching/FetchService.cs b/src/GenericQueryable.Runtime/Fetching/FetchService.cs
new file mode 100644
index 0000000..38d1be3
--- /dev/null
+++ b/src/GenericQueryable.Runtime/Fetching/FetchService.cs
@@ -0,0 +1,63 @@
+using System.Collections.Concurrent;
+using System.Linq.Expressions;
+using System.Reflection;
+
+using CommonFramework;
+using CommonFramework.ExpressionEvaluate;
+
+using Microsoft.Extensions.DependencyInjection;
+
+namespace GenericQueryable.Fetching;
+
+public abstract class FetchService([FromKeyedServices(RootFetchRuleExpander.Key)] IFetchRuleExpander fetchRuleExpander) : IFetchService
+{
+ private readonly ConcurrentDictionary> rootCache = [];
+
+ public virtual IQueryable ApplyFetch(IQueryable source, FetchRule fetchRule)
+ where TSource : class => this.GetApplyFetchFunc(fetchRule).Invoke(source);
+
+ private Func, IQueryable> GetApplyFetchFunc(FetchRule fetchRule)
+ where TSource : class
+ {
+ return this.rootCache
+ .GetOrAdd(typeof(TSource), _ => new ConcurrentDictionary())
+ .GetOrAdd(fetchRule.GetType(), _ => new ConcurrentDictionary, Func, IQueryable>>())
+ .Pipe(v => (ConcurrentDictionary, Func, IQueryable>>)v)
+ .GetOrAdd(fetchRule, _ =>
+ {
+ var fetchExpr = this.GetApplyFetchExpression(fetchRuleExpander.Expand(fetchRule));
+
+ return fetchExpr.Compile();
+ });
+ }
+
+ private Expression, IQueryable>> GetApplyFetchExpression(PropertyFetchRule fetchRule)
+ where TSource : class
+ {
+ var startState = ExpressionHelper.GetIdentity>();
+
+ return fetchRule.Paths.Aggregate(startState, (state, path) =>
+ {
+ var nextApplyFunc = this.GetApplyFetchExpression(path);
+
+ return ExpressionEvaluateHelper.InlineEvaluate, IQueryable>>(ee =>
+
+ q => ee.Evaluate(nextApplyFunc, ee.Evaluate(state, q)));
+ });
+ }
+
+ private Expression, IQueryable>> GetApplyFetchExpression(LambdaExpressionPath fetchPath)
+ where TSource : class
+ {
+ LambdaExpression startState = ExpressionHelper.GetIdentity>();
+
+ var resultBody = this
+ .GetFetchMethods(fetchPath).ZipStrong(fetchPath.Properties, (method, prop) => new { method, prop })
+ .Aggregate(startState.Body, (state, pair) => Expression.Call(pair.method, state, pair.prop));
+
+ return Expression.Lambda, IQueryable>>(resultBody, startState.Parameters);
+ }
+
+ protected abstract IEnumerable GetFetchMethods(LambdaExpressionPath fetchPath)
+ where TSource : class;
+}
\ No newline at end of file
diff --git a/src/GenericQueryable.Runtime/Fetching/UntypedFetchExpander.cs b/src/GenericQueryable.Runtime/Fetching/UntypedFetchExpander.cs
new file mode 100644
index 0000000..8320e81
--- /dev/null
+++ b/src/GenericQueryable.Runtime/Fetching/UntypedFetchExpander.cs
@@ -0,0 +1,29 @@
+using System.Collections.Concurrent;
+
+using CommonFramework;
+
+namespace GenericQueryable.Fetching;
+
+public class UntypedFetchExpander : IFetchRuleExpander
+{
+ private readonly ConcurrentDictionary cache = new();
+
+ public PropertyFetchRule? TryExpand(FetchRule fetchRule)
+ {
+ if (fetchRule is UntypedFetchRule untypedFetchRule)
+ {
+ return this.cache.GetOrAdd(typeof(TSource), _ => new ConcurrentDictionary, PropertyFetchRule>())
+ .Pipe(innerCache => (ConcurrentDictionary, PropertyFetchRule>)innerCache)
+ .Pipe(innerCache => innerCache.GetOrAdd(
+ untypedFetchRule,
+ _ =>
+ {
+ var fetchPath = LambdaExpressionPath.Create(typeof(TSource), untypedFetchRule.Path.Split('.'));
+
+ return new PropertyFetchRule([fetchPath]);
+ }));
+ }
+
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/src/GenericQueryable.Runtime/GenericQueryable.Runtime.csproj b/src/GenericQueryable.Runtime/GenericQueryable.Runtime.csproj
index 6b15324..3ee59ac 100644
--- a/src/GenericQueryable.Runtime/GenericQueryable.Runtime.csproj
+++ b/src/GenericQueryable.Runtime/GenericQueryable.Runtime.csproj
@@ -5,6 +5,10 @@
GenericQueryable
+
+
+
+
diff --git a/src/GenericQueryable/DependencyInjection/GenericQueryableSetup.cs b/src/GenericQueryable/DependencyInjection/GenericQueryableSetup.cs
index dc00a54..a0373e3 100644
--- a/src/GenericQueryable/DependencyInjection/GenericQueryableSetup.cs
+++ b/src/GenericQueryable/DependencyInjection/GenericQueryableSetup.cs
@@ -26,6 +26,8 @@ public void Initialize(IServiceCollection services)
services.TryAddSingleton();
services.AddSingleton();
+ services.AddSingleton();
+
services.AddKeyedSingleton(RootFetchRuleExpander.Key);
}
diff --git a/src/__SolutionItems/CommonAssemblyInfo.cs b/src/__SolutionItems/CommonAssemblyInfo.cs
index 1df93de..4fdb881 100644
--- a/src/__SolutionItems/CommonAssemblyInfo.cs
+++ b/src/__SolutionItems/CommonAssemblyInfo.cs
@@ -3,7 +3,7 @@
[assembly: AssemblyProduct("GenericQueryable")]
[assembly: AssemblyCompany("IvAt")]
-[assembly: AssemblyVersion("2.1.11.0")]
+[assembly: AssemblyVersion("2.1.13.0")]
[assembly: AssemblyInformationalVersion("changes at build")]
#if DEBUG