fix some routing problems
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace zero.Core.Extensions
|
||||
{
|
||||
@@ -22,5 +23,18 @@ namespace zero.Core.Extensions
|
||||
object value = model.GetValueOrDefault(key);
|
||||
return value == default || !(value is T) ? default : (T)value;
|
||||
}
|
||||
|
||||
|
||||
public static Dictionary<TKey, TElement> ToDistinctDictionary<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector)
|
||||
{
|
||||
Dictionary<TKey, TElement> result = new();
|
||||
|
||||
foreach (TSource sourceElement in source)
|
||||
{
|
||||
result.TryAdd(keySelector(sourceElement), elementSelector(sourceElement));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ namespace zero.Core.Routing
|
||||
{
|
||||
public static string PAGE_TYPE_PARAM = "pageType";
|
||||
|
||||
public static string PAGE_ID_PARAM = "pageId";
|
||||
|
||||
protected IPageUrlBuilder UrlBuilder { get; set; }
|
||||
|
||||
|
||||
@@ -47,6 +49,7 @@ namespace zero.Core.Routing
|
||||
route.DependsOn(model.Id);
|
||||
route.DependsOn(parents.Select(x => x.Id).ToArray());
|
||||
route.Param(PAGE_TYPE_PARAM, model.PageTypeAlias);
|
||||
route.Param(PAGE_ID_PARAM, model.Id);
|
||||
|
||||
return route;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,12 @@ namespace zero.Core.Routing
|
||||
}
|
||||
|
||||
|
||||
public async Task<Page> ResolvePageFor<T>(RoutingContext context, Func<Page, bool> predicate = null)
|
||||
{
|
||||
return (await ResolveAllPagesFor<T>(context, predicate)).FirstOrDefault();
|
||||
}
|
||||
|
||||
|
||||
public async Task<HashSet<Route>> ResolveAllFor<T>(RoutingContext context, Func<Page, bool> predicate = null)
|
||||
{
|
||||
string fullKey = typeof(T).Name.ToString().ToLower() + ":" + context.Context.AppId;
|
||||
@@ -57,5 +63,44 @@ namespace zero.Core.Routing
|
||||
|
||||
return routes.Select(x => x.Item2).ToHashSet();
|
||||
}
|
||||
|
||||
|
||||
public async Task<HashSet<Page>> ResolveAllPagesFor<T>(RoutingContext context, Func<Page, bool> predicate = null)
|
||||
{
|
||||
string fullKey = typeof(T).Name.ToString().ToLower() + ":" + context.Context.AppId;
|
||||
|
||||
if (!PageRouteCache.TryGetValue(fullKey, out HashSet<(Page, Route)> routes))
|
||||
{
|
||||
List<Page> pages = new();
|
||||
IEnumerable<Expression<Func<Page, bool>>> resolvers = context.Context.Options.Routing.PageResolvers.GetAll(typeof(T));
|
||||
|
||||
foreach (Expression<Func<Page, bool>> resolver in resolvers.Reverse())
|
||||
{
|
||||
pages.AddRange(await context.Session.Query<Page>().Where(resolver).ToListAsync());
|
||||
}
|
||||
|
||||
Dictionary<string, Page> idToPage = pages.ToDictionary(x => context.Context.Options.Routing.PageRouteIdBuilder.Generate(x), x => x);
|
||||
Dictionary<string, Route> idToRoute = await context.Session.LoadAsync<Route>(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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace zero.Core.Routing
|
||||
|
||||
// delete previous route if the ID has changed,
|
||||
// otherwise it will just be updated
|
||||
if (previousModel != null && !route.Id.Equals(GetId(provider, previousModel)))
|
||||
if (previousModel != null && !route.Id.Equals(provider.Id(previousModel)))
|
||||
{
|
||||
context.Session.Delete(previousModel);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Raven.Client.Documents;
|
||||
using Raven.Client.Documents.Linq;
|
||||
using Raven.Client.Documents.Session;
|
||||
using Raven.Client.Exceptions.Documents.Indexes;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -15,24 +14,16 @@ using zero.Core.Options;
|
||||
|
||||
namespace zero.Core.Routing
|
||||
{
|
||||
public class RouteResolver : IRouteResolver
|
||||
public class RouteResolver : Routes, IRouteResolver
|
||||
{
|
||||
public const char PATH_SEPERATOR = '/';
|
||||
|
||||
protected IZeroStore Store { get; set; }
|
||||
protected ILogger<Routes> Logger { get; set; }
|
||||
protected IEnumerable<IRouteProvider> Providers { get; set; }
|
||||
protected IApplicationResolver AppResolver { get; set; }
|
||||
protected IZeroOptions Options { get; set; }
|
||||
|
||||
|
||||
public RouteResolver(IZeroStore store, ILogger<Routes> logger, IEnumerable<IRouteProvider> providers, IApplicationResolver appResolver, IZeroOptions options)
|
||||
public RouteResolver(IZeroContext context, IZeroStore store, ILogger<Routes> logger, IEnumerable<IRouteProvider> providers, IApplicationResolver appResolver) : base(context, store, logger, providers)
|
||||
{
|
||||
Store = store;
|
||||
Logger = logger;
|
||||
Providers = providers;
|
||||
AppResolver = appResolver;
|
||||
Options = options;
|
||||
Options = context.Options;
|
||||
}
|
||||
|
||||
|
||||
@@ -95,7 +86,7 @@ namespace zero.Core.Routing
|
||||
return null;
|
||||
}
|
||||
|
||||
return await ResolveRouteInternal(session, new RouteResponse()
|
||||
return await ResolveRouteInternal(new RouteResponse()
|
||||
{
|
||||
Route = route,
|
||||
App = application,
|
||||
@@ -128,36 +119,26 @@ namespace zero.Core.Routing
|
||||
/// <inheritdoc />
|
||||
public RouteEndpoint MapEndpoint(IRouteModel route)
|
||||
{
|
||||
IRouteProvider routeProvider = FindProvider(route.Route.ProviderAlias);
|
||||
if (TryGetProvider(route.Route.ProviderAlias, out IRouteProvider provider))
|
||||
{
|
||||
return provider.Map(GetContext(), route);
|
||||
}
|
||||
|
||||
return null;
|
||||
//return routeProvider?.MapEndpoint(route);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Call the provider which can resolve the route
|
||||
/// </summary>
|
||||
Task<IRouteModel> ResolveRouteInternal(IAsyncDocumentSession session, RouteResponse response)
|
||||
async Task<IRouteModel> ResolveRouteInternal(RouteResponse response)
|
||||
{
|
||||
IRouteProvider routeProvider = FindProvider(response.Route.ProviderAlias);
|
||||
return Task.FromResult(default(IRouteModel));
|
||||
//return await routeProvider?.ResolveRoute(session, response);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Find registered route provider for the specified alias
|
||||
/// </summary>
|
||||
IRouteProvider FindProvider(string alias)
|
||||
{
|
||||
IRouteProvider routeProvider = Providers.FirstOrDefault(x => x.Alias == alias);
|
||||
|
||||
if (routeProvider == null)
|
||||
if (TryGetProvider(response.Route.ProviderAlias, out IRouteProvider provider))
|
||||
{
|
||||
Logger.LogWarning("Could not locate URL provider {provider}", alias);
|
||||
return await provider.Model(GetContext(), response.Route);
|
||||
}
|
||||
|
||||
return routeProvider;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+29
-62
@@ -6,7 +6,6 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using zero.Core.Database;
|
||||
using zero.Core.Entities;
|
||||
using zero.Core.Extensions;
|
||||
|
||||
|
||||
namespace zero.Core.Routing
|
||||
@@ -31,47 +30,27 @@ namespace zero.Core.Routing
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<string> GetUrl<T>(T model, object parameters = null) where T : IZeroRouteEntity => (await GetRoute(model, parameters))?.Url;
|
||||
public async Task<string> GetUrl<T>(T model) where T : IZeroRouteEntity => (await GetRoute(model))?.Url;
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<string> GetUrl<T>(string id, object parameters = null) where T : IZeroRouteEntity => (await GetRoute<T>(id, parameters))?.Url;
|
||||
public async Task<string> GetUrl<T>(string id) where T : IZeroRouteEntity => (await GetRoute<T>(id))?.Url;
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Dictionary<T, string>> GetUrls<T>(IEnumerable<T> models, object parameters = null) where T : IZeroRouteEntity => (await GetRoutes(models, parameters)).ToDictionary(x => x.Key, x => x.Value?.Url);
|
||||
public async Task<Dictionary<T, string>> GetUrls<T>(params T[] models) where T : IZeroRouteEntity => (await GetRoutes(models)).ToDictionary(x => x.Key, x => x.Value?.Url);
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Route> GetRoute<T>(string id, object parameters = null) where T : IZeroRouteEntity
|
||||
public async Task<Route> GetRoute<T>(string id) where T : IZeroRouteEntity
|
||||
{
|
||||
if (id.IsNullOrEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Type type = typeof(T);
|
||||
IRouteProvider routeProvider = Providers.FirstOrDefault(x => x.CanHandle(type));
|
||||
|
||||
if (routeProvider == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
RoutingContext context = GetContext();
|
||||
T model = await context.Session.LoadAsync<T>(id);
|
||||
|
||||
if (model == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return await FindRoute(routeProvider, context, model);
|
||||
T model = await GetContext().Session.LoadAsync<T>(id);
|
||||
return await GetRoute(model);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Route> GetRoute<T>(T model, object parameters = null) where T : IZeroRouteEntity
|
||||
public async Task<Route> GetRoute<T>(T model) where T : IZeroRouteEntity
|
||||
{
|
||||
if (model == null)
|
||||
{
|
||||
@@ -89,9 +68,9 @@ namespace zero.Core.Routing
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Dictionary<T, Route>> GetRoutes<T>(IEnumerable<T> models, object parameters = null) where T : IZeroRouteEntity
|
||||
public async Task<Dictionary<T, Route>> GetRoutes<T>(params T[] models) where T : IZeroRouteEntity
|
||||
{
|
||||
if (!models.Any())
|
||||
if (models.Length < 1)
|
||||
{
|
||||
return new();
|
||||
}
|
||||
@@ -109,16 +88,7 @@ namespace zero.Core.Routing
|
||||
}
|
||||
|
||||
RoutingContext context = GetContext();
|
||||
Dictionary<string, T> idMap = models.ToDictionary(x => GetId(routeProvider, x), x => x);
|
||||
Dictionary<string, Route> routes = await context.Session.LoadAsync<Route>(idMap.Select(x => x.Key));
|
||||
Dictionary<T, Route> result = new();
|
||||
|
||||
foreach ((string id, T model) in idMap)
|
||||
{
|
||||
result.Add(model, routes.GetValueOrDefault(id));
|
||||
}
|
||||
|
||||
return result;
|
||||
return (await routeProvider.Find(context, models.Select(x => (IZeroRouteEntity)x).ToArray())).ToDictionary(x => (T)x.Key, x => x.Value);
|
||||
}
|
||||
|
||||
|
||||
@@ -131,6 +101,14 @@ namespace zero.Core.Routing
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual bool TryGetProvider(string alias, out IRouteProvider provider)
|
||||
{
|
||||
provider = Providers.FirstOrDefault(x => x.Alias.Equals(alias, StringComparison.InvariantCultureIgnoreCase));
|
||||
return provider != null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Map a route to an MVC endpoint
|
||||
/// </summary>
|
||||
@@ -161,22 +139,6 @@ namespace zero.Core.Routing
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get ID in collection for an entity
|
||||
/// </summary>
|
||||
protected virtual string GetId(IRouteProvider provider, IZeroRouteEntity model)
|
||||
{
|
||||
string key = provider.Key(model);
|
||||
|
||||
if (key.IsNullOrEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return "routes." + provider.Alias + "." + key;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Build a new routing context
|
||||
/// </summary>
|
||||
@@ -191,36 +153,41 @@ namespace zero.Core.Routing
|
||||
/// <summary>
|
||||
/// Get the URL for an entity
|
||||
/// </summary>
|
||||
Task<string> GetUrl<T>(T model, object parameters = null) where T : IZeroRouteEntity;
|
||||
Task<string> GetUrl<T>(T model) where T : IZeroRouteEntity;
|
||||
|
||||
/// <summary>
|
||||
/// Get the URL for an entity
|
||||
/// </summary>
|
||||
Task<string> GetUrl<T>(string id, object parameters = null) where T : IZeroRouteEntity;
|
||||
Task<string> GetUrl<T>(string id) where T : IZeroRouteEntity;
|
||||
|
||||
/// <summary>
|
||||
/// Get URLs for multiple entities
|
||||
/// </summary>
|
||||
Task<Dictionary<T, string>> GetUrls<T>(IEnumerable<T> models, object parameters = null) where T : IZeroRouteEntity;
|
||||
Task<Dictionary<T, string>> GetUrls<T>(params T[] models) where T : IZeroRouteEntity;
|
||||
|
||||
/// <summary>
|
||||
/// Get the route object for an entity
|
||||
/// </summary>
|
||||
Task<Route> GetRoute<T>(T model, object parameters = null) where T : IZeroRouteEntity;
|
||||
Task<Route> GetRoute<T>(T model) where T : IZeroRouteEntity;
|
||||
|
||||
/// <summary>
|
||||
/// Get the route object for an entity
|
||||
/// </summary>
|
||||
Task<Route> GetRoute<T>(string id, object parameters = null) where T : IZeroRouteEntity;
|
||||
Task<Route> GetRoute<T>(string id) where T : IZeroRouteEntity;
|
||||
|
||||
/// <summary>
|
||||
/// Get routes for multiple entities
|
||||
/// </summary>
|
||||
Task<Dictionary<T, Route>> GetRoutes<T>(IEnumerable<T> models, object parameters = null) where T : IZeroRouteEntity;
|
||||
Task<Dictionary<T, Route>> GetRoutes<T>(params T[] models) where T : IZeroRouteEntity;
|
||||
|
||||
/// <summary>
|
||||
/// Find a provider for a certain entity
|
||||
/// </summary>
|
||||
bool TryGetProvider<T>(T model, out IRouteProvider provider) where T : IZeroRouteEntity;
|
||||
|
||||
/// <summary>
|
||||
/// Find a provider by alias
|
||||
/// </summary>
|
||||
bool TryGetProvider(string alias, out IRouteProvider provider);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using zero.Core.Entities;
|
||||
using zero.Core.Extensions;
|
||||
|
||||
namespace zero.Core.Routing
|
||||
{
|
||||
@@ -42,6 +43,27 @@ namespace zero.Core.Routing
|
||||
|
||||
/// <inheritdoc />
|
||||
public sealed override Task<Route> Find(RoutingContext context, IZeroRouteEntity model) => Find(context, (T)model);
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual async Task<Dictionary<T, Route>> Find(RoutingContext context, params T[] models)
|
||||
{
|
||||
Dictionary<string, T> idMap = models.ToDistinctDictionary(x => Id(x), x => x);
|
||||
Dictionary<string, Route> routes = await context.Session.LoadAsync<Route>(idMap.Select(x => x.Key));
|
||||
Dictionary<T, Route> result = new();
|
||||
|
||||
foreach ((string id, T model) in idMap)
|
||||
{
|
||||
result.Add(model, routes.GetValueOrDefault(id));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public sealed override async Task<Dictionary<IZeroRouteEntity, Route>> Find(RoutingContext context, params IZeroRouteEntity[] models)
|
||||
{
|
||||
return (await Find(context, models.Select(x => (T)x).ToArray())).ToDictionary(x => (IZeroRouteEntity)x.Key, x => x.Value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -97,10 +119,23 @@ namespace zero.Core.Routing
|
||||
return context.Context.Options.Routing.DefaultEndpoint;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find a persisted route for an entity
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public virtual async Task<Route> Find(RoutingContext context, IZeroRouteEntity model) => await context.Session.LoadAsync<Route>(Id(model));
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual async Task<Dictionary<IZeroRouteEntity, Route>> Find(RoutingContext context, params IZeroRouteEntity[] models)
|
||||
{
|
||||
Dictionary<string, IZeroRouteEntity> idMap = models.ToDistinctDictionary(x => Id(x), x => x);
|
||||
Dictionary<string, Route> routes = await context.Session.LoadAsync<Route>(idMap.Select(x => x.Key));
|
||||
Dictionary<IZeroRouteEntity, Route> result = new();
|
||||
|
||||
foreach ((string id, IZeroRouteEntity model) in idMap)
|
||||
{
|
||||
result.Add(model, routes.GetValueOrDefault(id));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -186,6 +221,11 @@ namespace zero.Core.Routing
|
||||
/// Find a persisted route for an entity
|
||||
/// </summary>
|
||||
Task<Route> Find(RoutingContext context, IZeroRouteEntity model);
|
||||
|
||||
/// <summary>
|
||||
/// Find persisted routes for entities
|
||||
/// </summary>
|
||||
Task<Dictionary<IZeroRouteEntity, Route>> Find(RoutingContext context, params IZeroRouteEntity[] models);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace zero.Web.Controllers
|
||||
{
|
||||
IReadOnlyCollection<PageType> pageTypes = Options.Pages.GetAllItems();
|
||||
Dictionary<string, Page> pages = await Collection.GetByIds(ids.ToArray());
|
||||
Dictionary<Page, Route> routes = await Routes.GetRoutes(pages.Where(x => x.Value != null).Select(x => x.Value));
|
||||
Dictionary<Page, Route> routes = await Routes.GetRoutes(pages.Where(x => x.Value != null).Select(x => x.Value).ToArray());
|
||||
|
||||
return Previews(pages, item =>
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user