using Raven.Client.Documents; using Raven.Client.Documents.Linq; using System.Collections.Concurrent; using System.Linq.Expressions; namespace zero.Routing; public class PageRouteResolverHelper { protected ConcurrentDictionary> PageRouteCache { get; set; } = new(); public async Task ResolveFor(RoutingContext context, Func predicate = null) { return (await ResolveAllFor(context, predicate)).FirstOrDefault(); } public async Task ResolvePageFor(RoutingContext context, Func predicate = null) { return (await ResolveAllPagesFor(context, predicate)).FirstOrDefault(); } public async Task> ResolveAllFor(RoutingContext context, Func predicate = null) { RoutingOptions options = context.Context.Options.For(); string fullKey = typeof(T).Name.ToString().ToLower() + ":" + context.Context.AppId; if (!PageRouteCache.TryGetValue(fullKey, out HashSet<(Page, Route)> routes)) { List pages = new(); IEnumerable>> resolvers = options.PageResolvers.GetAll(typeof(T)); foreach (Expression> resolver in resolvers.Reverse()) { pages.AddRange(await context.Session.Query().Where(resolver).ToListAsync()); } Dictionary idToPage = pages.ToDictionary(x => options.PageRouteIdBuilder.Generate(x), x => x); Dictionary idToRoute = await context.Session.LoadAsync(idToPage.Select(x => x.Key)); routes = new(); foreach ((string id, Page page) in idToPage) { if (idToRoute.TryGetValue(id, out Route route) && route != null) { routes.Add((page, route)); } } PageRouteCache.TryAdd(fullKey, routes); } if (predicate != null) { return routes.Where(x => predicate(x.Item1)).Select(x => x.Item2).ToHashSet(); } return routes.Select(x => x.Item2).ToHashSet(); } public async Task> ResolveAllPagesFor(RoutingContext context, Func predicate = null) { RoutingOptions options = context.Context.Options.For(); string fullKey = typeof(T).Name.ToString().ToLower() + ":" + context.Context.AppId; if (!PageRouteCache.TryGetValue(fullKey, out HashSet<(Page, Route)> routes)) { List pages = new(); IEnumerable>> resolvers = options.PageResolvers.GetAll(typeof(T)); foreach (Expression> resolver in resolvers.Reverse()) { pages.AddRange(await context.Session.Query().Where(resolver).ToListAsync()); } Dictionary idToPage = pages.ToDictionary(x => options.PageRouteIdBuilder.Generate(x), x => x); Dictionary idToRoute = await context.Session.LoadAsync(idToPage.Select(x => x.Key)); routes = new(); foreach ((string id, Page page) in idToPage) { if (idToRoute.TryGetValue(id, out Route route) && route != null) { routes.Add((page, route)); } } PageRouteCache.TryAdd(fullKey, routes); } if (predicate != null) { return routes.Where(x => predicate(x.Item1)).Select(x => x.Item1).ToHashSet(); } return routes.Select(x => x.Item1).ToHashSet(); } public async Task> ResolveAllForAsDictionary(RoutingContext context, Func<(Page, Route), bool> predicate = null) { RoutingOptions options = context.Context.Options.For(); string fullKey = typeof(T).Name.ToString().ToLower() + ":" + context.Context.AppId; if (!PageRouteCache.TryGetValue(fullKey, out HashSet<(Page, Route)> routes)) { List pages = new(); IEnumerable>> resolvers = options.PageResolvers.GetAll(typeof(T)); foreach (Expression> resolver in resolvers.Reverse()) { pages.AddRange(await context.Session.Query().Where(resolver).ToListAsync()); } Dictionary idToPage = pages.ToDictionary(x => options.PageRouteIdBuilder.Generate(x), x => x); Dictionary idToRoute = await context.Session.LoadAsync(idToPage.Select(x => x.Key)); routes = new(); foreach ((string id, Page page) in idToPage) { if (idToRoute.TryGetValue(id, out Route route) && route != null) { routes.Add((page, route)); } } PageRouteCache.TryAdd(fullKey, routes); } if (predicate != null) { return routes.Where(x => predicate(x)).ToDictionary(x => x.Item1, x => x.Item2); } return routes.ToDictionary(x => x.Item1, x => x.Item2); } public async Task IsRelevantFor(RoutingContext context, Page page) { HashSet pages = await ResolveAllPagesFor(context); if (pages.Any(x => x.Id == page.Id)) { return true; } string[] childIds = await GetChildIds(context, page.Id); return pages.Any(x => childIds.Contains(x.Id)); } /// /// Get relevant pages for a page /// public virtual async Task> GetRelevantPagesFor(RoutingContext context, Page page) { HashSet pages = await ResolveAllPagesFor(context); string[] childIds = await GetChildIds(context, page.Id); return pages.Where(x => childIds.Contains(x.Id) || x.Id == page.Id).ToList(); } /// /// Get parents for a page /// protected virtual async Task GetChildIds(RoutingContext context, params string[] pageIds) { return await context.Session.Query() .Where(x => x.PathIds.In(pageIds)) .Select(x => x.Id) .ToArrayAsync(); } }