diff --git a/src/GenericQueryable.EntityFramework/EfFetchService.cs b/src/GenericQueryable.EntityFramework/EfFetchService.cs index 761bd02..fcef9b9 100644 --- a/src/GenericQueryable.EntityFramework/EfFetchService.cs +++ b/src/GenericQueryable.EntityFramework/EfFetchService.cs @@ -7,27 +7,28 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Query; +using Microsoft.Extensions.DependencyInjection; namespace GenericQueryable.EntityFramework; -public class EfFetchService(IEnumerable expanders) : IFetchService +public class EfFetchService([FromKeyedServices(RootFetchRuleExpander.Key)] IFetchRuleExpander fetchRuleExpander) : IFetchService { - public virtual IQueryable ApplyFetch(IQueryable source, FetchRule fetchRule) - where TSource : class + public virtual IQueryable ApplyFetch(IQueryable source, FetchRule fetchRule) + where TSource : class { - var expandedFetchRule = expanders.Aggregate(fetchRule, (state, expander) => expander.TryExpand(state) ?? state); + var expandedFetchRule = fetchRuleExpander.TryExpand(fetchRule) ?? fetchRule; return expandedFetchRule switch - { - UntypedFetchRule untypedFetchRule => source.Include(untypedFetchRule.Path), + { + UntypedFetchRule untypedFetchRule => source.Include(untypedFetchRule.Path), - PropertyFetchRule propertyFetchRule => this.ApplyFetch(source, propertyFetchRule), + PropertyFetchRule propertyFetchRule => this.ApplyFetch(source, propertyFetchRule), - _ => throw new ArgumentOutOfRangeException(nameof(fetchRule)) - }; - } + _ => throw new ArgumentOutOfRangeException(nameof(fetchRule)) + }; + } - protected IQueryable ApplyFetch(IQueryable source, PropertyFetchRule fetchRule) + protected IQueryable ApplyFetch(IQueryable source, PropertyFetchRule fetchRule) where TSource : class { return fetchRule.Paths.Aggregate(source, this.ApplyFetch); diff --git a/src/GenericQueryable.IntegrationTests/AppFetchRule.cs b/src/GenericQueryable.IntegrationTests/AppFetchRule.cs index cd20b04..67c73cc 100644 --- a/src/GenericQueryable.IntegrationTests/AppFetchRule.cs +++ b/src/GenericQueryable.IntegrationTests/AppFetchRule.cs @@ -5,5 +5,5 @@ namespace GenericQueryable.IntegrationTests; public static class AppFetchRule { - public static FetchRule TestFetchRule { get; } = new FetchRuleHeader(nameof(TestFetchRule)); + public static FetchRuleHeader TestFetchRule { get; } = new FetchRuleHeader(nameof(TestFetchRule)); } \ No newline at end of file diff --git a/src/GenericQueryable/DependencyInjection/GenericQueryableSetup.cs b/src/GenericQueryable/DependencyInjection/GenericQueryableSetup.cs index 6b5bf48..efa2109 100644 --- a/src/GenericQueryable/DependencyInjection/GenericQueryableSetup.cs +++ b/src/GenericQueryable/DependencyInjection/GenericQueryableSetup.cs @@ -23,6 +23,8 @@ public void Initialize(IServiceCollection services) services.AddSingleton(typeof(IFetchService), this.fetchServiceType); services.AddSingleton(typeof(ITargetMethodExtractor), this.targetMethodExtractorType); + services.AddKeyedSingleton(RootFetchRuleExpander.Key); + foreach (var fetchRuleExpanderType in this.fetchRuleExpanderTypeList) { services.AddSingleton(typeof(IFetchRuleExpander), fetchRuleExpanderType); @@ -42,7 +44,7 @@ public IGenericQueryableSetup SetFetchService() return this; } - public IGenericQueryableSetup AddFetchRule(FetchRule header, FetchRule implementation) + public IGenericQueryableSetup AddFetchRule(FetchRuleHeader header, PropertyFetchRule implementation) { this.fetchRuleHeaderInfoList.Add(new FetchRuleHeaderInfo(header, implementation)); diff --git a/src/GenericQueryable/DependencyInjection/IGenericQueryableSetup.cs b/src/GenericQueryable/DependencyInjection/IGenericQueryableSetup.cs index cb834d4..bad5ebb 100644 --- a/src/GenericQueryable/DependencyInjection/IGenericQueryableSetup.cs +++ b/src/GenericQueryable/DependencyInjection/IGenericQueryableSetup.cs @@ -11,7 +11,7 @@ IGenericQueryableSetup SetFetchService() IGenericQueryableSetup AddFetchRuleExpander() where TFetchRuleExpander : IFetchRuleExpander; - IGenericQueryableSetup AddFetchRule(FetchRule header, FetchRule implementation); + IGenericQueryableSetup AddFetchRule(FetchRuleHeader header, PropertyFetchRule implementation); IGenericQueryableSetup SetTargetMethodExtractor() where TTargetMethodExtractor : ITargetMethodExtractor; diff --git a/src/GenericQueryable/Fetching/FetchRuleHeader.cs b/src/GenericQueryable/Fetching/FetchRuleHeader.cs index f890f6b..827c273 100644 --- a/src/GenericQueryable/Fetching/FetchRuleHeader.cs +++ b/src/GenericQueryable/Fetching/FetchRuleHeader.cs @@ -1,3 +1,5 @@ namespace GenericQueryable.Fetching; -public record FetchRuleHeader(string Name) : FetchRule; \ No newline at end of file +public abstract record FetchRuleHeader : FetchRule; + +public record FetchRuleHeader(TValue Value) : FetchRuleHeader; \ No newline at end of file diff --git a/src/GenericQueryable/Fetching/FetchRuleHeaderExpander.cs b/src/GenericQueryable/Fetching/FetchRuleHeaderExpander.cs index 0f34818..3378de3 100644 --- a/src/GenericQueryable/Fetching/FetchRuleHeaderExpander.cs +++ b/src/GenericQueryable/Fetching/FetchRuleHeaderExpander.cs @@ -11,17 +11,24 @@ public class FetchRuleHeaderExpander(IEnumerable fetchRuleH private readonly ConcurrentDictionary cache = new(); - public FetchRule? TryExpand(FetchRule fetchRule) + public PropertyFetchRule? TryExpand(FetchRule fetchRule) { - return cache.GetOrAdd(typeof(TSource), - _ => headersDict - .GetValueOrDefault(typeof(TSource)) - .EmptyIfNull() - .Cast>() - .ToDictionary(info => info.Header, info => info.Implementation)) + if (fetchRule is FetchRuleHeader fetchRuleHeader) + { + return cache.GetOrAdd(typeof(TSource), + _ => headersDict + .GetValueOrDefault(typeof(TSource)) + .EmptyIfNull() + .Cast>() + .ToDictionary(info => info.Header, info => info.Implementation)) - .Pipe(innerCache => (IReadOnlyDictionary, FetchRule>)innerCache) + .Pipe(innerCache => (IReadOnlyDictionary, PropertyFetchRule>)innerCache) - .Pipe(innerCache => innerCache.GetValueOrDefault(fetchRule)); + .Pipe(innerCache => innerCache.GetValueOrDefault(fetchRuleHeader)); + } + else + { + return null; + } } } \ No newline at end of file diff --git a/src/GenericQueryable/Fetching/FetchRuleHeaderInfo.cs b/src/GenericQueryable/Fetching/FetchRuleHeaderInfo.cs index e63b805..42c6dfc 100644 --- a/src/GenericQueryable/Fetching/FetchRuleHeaderInfo.cs +++ b/src/GenericQueryable/Fetching/FetchRuleHeaderInfo.cs @@ -5,7 +5,7 @@ public abstract record FetchRuleHeaderInfo public abstract Type SourceType { get; } } -public record FetchRuleHeaderInfo(FetchRule Header, FetchRule Implementation) : FetchRuleHeaderInfo +public record FetchRuleHeaderInfo(FetchRuleHeader Header, PropertyFetchRule Implementation) : FetchRuleHeaderInfo { public override Type SourceType { get; } = typeof(TSource); } \ No newline at end of file diff --git a/src/GenericQueryable/Fetching/IFetchRuleExpander.cs b/src/GenericQueryable/Fetching/IFetchRuleExpander.cs index db85e27..325705d 100644 --- a/src/GenericQueryable/Fetching/IFetchRuleExpander.cs +++ b/src/GenericQueryable/Fetching/IFetchRuleExpander.cs @@ -2,5 +2,5 @@ public interface IFetchRuleExpander { - FetchRule? TryExpand(FetchRule fetchRule); + PropertyFetchRule? TryExpand(FetchRule fetchRule); } \ No newline at end of file diff --git a/src/GenericQueryable/Fetching/RootFetchRuleExpander.cs b/src/GenericQueryable/Fetching/RootFetchRuleExpander.cs new file mode 100644 index 0000000..aa04970 --- /dev/null +++ b/src/GenericQueryable/Fetching/RootFetchRuleExpander.cs @@ -0,0 +1,40 @@ +using System.Collections.Concurrent; + +using CommonFramework; + +namespace GenericQueryable.Fetching; + +public class RootFetchRuleExpander(IEnumerable expanders) : IFetchRuleExpander +{ + public const string Key = "Root"; + + private readonly ConcurrentDictionary cache = new(); + + public PropertyFetchRule? TryExpand(FetchRule fetchRule) + { + if (fetchRule is PropertyFetchRule propertyFetchRule) + { + return propertyFetchRule; + } + else + { + return cache + .GetOrAdd(typeof(TSource), _ => new ConcurrentDictionary, PropertyFetchRule?>()) + .Pipe(innerCache => (ConcurrentDictionary, PropertyFetchRule?>)innerCache) + .GetOrAdd(fetchRule, _ => + { + var request = + + from expander in expanders + + let expandedFetchRule = expander.TryExpand(fetchRule) + + where expandedFetchRule != null + + select expandedFetchRule; + + return request.FirstOrDefault(); + }); + } + } +} \ No newline at end of file diff --git a/src/__SolutionItems/CommonAssemblyInfo.cs b/src/__SolutionItems/CommonAssemblyInfo.cs index 436881a..6d3d68b 100644 --- a/src/__SolutionItems/CommonAssemblyInfo.cs +++ b/src/__SolutionItems/CommonAssemblyInfo.cs @@ -3,7 +3,7 @@ [assembly: AssemblyProduct("GenericQueryable")] [assembly: AssemblyCompany("IvAt")] -[assembly: AssemblyVersion("2.0.5.0")] +[assembly: AssemblyVersion("2.0.6.0")] [assembly: AssemblyInformationalVersion("changes at build")] #if DEBUG