using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Razor; using System.Collections.Concurrent; using System.Reflection; namespace MyWebLog.Features; /// /// A controller model convention that identifies the feature in which a controller exists /// public class FeatureControllerModelConvention : IControllerModelConvention { /// /// A cache of controller types to features /// private static readonly ConcurrentDictionary _features = new(); /// /// Derive the feature name from the controller's type /// private static string? GetFeatureName(TypeInfo typ) { var cacheKey = typ.FullName ?? ""; if (_features.ContainsKey(cacheKey)) return _features[cacheKey]; var tokens = cacheKey.Split('.'); if (tokens.Any(it => it == "Features")) { var feature = tokens.SkipWhile(it => it != "Features").Skip(1).Take(1).FirstOrDefault(); if (feature is not null) { _features[cacheKey] = feature; return feature; } } return null; } /// public void Apply(ControllerModel controller) => controller.Properties.Add("feature", GetFeatureName(controller.ControllerType)); } /// /// Expand the location token with the feature name /// public class FeatureViewLocationExpander : IViewLocationExpander { /// public IEnumerable ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable viewLocations) { _ = context ?? throw new ArgumentNullException(nameof(context)); _ = viewLocations ?? throw new ArgumentNullException(nameof(viewLocations)); if (context.ActionContext.ActionDescriptor is not ControllerActionDescriptor descriptor) throw new ArgumentException("ActionDescriptor not found"); var feature = descriptor.Properties["feature"] as string ?? ""; foreach (var location in viewLocations) yield return location.Replace("{2}", feature); } /// public void PopulateValues(ViewLocationExpanderContext _) { } }