ASP.Net Core 2 Webアプリケーションがあります。
カスタムルーティングミドルウェアを作成しようとしているので、データベースからルートを取得できます。
ConfigureServicesには次のものがあります。
services.AddDbContext<DbContext>(options => options.UseMySQL(configuration.GetConnectionString("ConnectionClient")));
services.AddScoped<IServiceConfig, ServiceConfig>();
構成で:
app.UseMvc(routes =>
{
routes.Routes.Add(new RouteCustom(routes.DefaultHandler);
routes.MapRoute(name: "default", template: "{controller=Home}/{action=Index}/{id?}");
});
RouteCustomで
public class RouteCustom : IRouteCustom
{
private readonly IRouter _innerRouter;
private IServiceConfig _serviceConfig;
public RouteCustom(IRouter innerRouter)
{
_innerRouter = innerRouter ?? throw new ArgumentNullException(nameof(innerRouter));
}
public async Task RouteAsync(RouteContext context)
{
_serviceConfig = context.HttpContext.RequestServices.GetRequiredService<IServiceConfig>();
...
Operations inside _serviceConfig to get the route
}
public VirtualPathData GetVirtualPath(VirtualPathContext context)
{
_serviceConfig = context.HttpContext.RequestServices.GetRequiredService<IServiceConfig>();
...
Operations inside _serviceConfig to get the route
}
}
IServiceConfigは、データベースにアクセスしてデータ(この場合はルート)を取得するだけでなく、アプリケーションに必要な構成データも含むクラスです。
public interface IServiceConfig
{
Config GetConfig();
List<RouteWeb> SelRoutesWeb();
}
public class ServiceConfig : IServiceConfig
{
private readonly IMemoryCache _memoryCache;
private readonly IUnitOfWork _unitOfWork;
private readonly IServiceTenant _serviceTenant;
public ServiceConfig(IMemoryCache memoryCache, IUnitOfWork unitOfWork, IServiceTenant serviceTenant)
{
_memoryCache = memoryCache;
_unitOfWork = unitOfWork;
_serviceTenant = serviceTenant;
}
public Config GetConfig()
{
var cacheConfigTenant = Names.CacheConfig + _serviceTenant.GetId();
var config = _memoryCache.Get<Config>(cacheConfigTenant);
if (config != null) return config;
config = _unitOfWork.Config.Get();
_memoryCache.Set(cacheConfigTenant, config, new MemoryCacheEntryOptions() { SlidingExpiration = Names.CacheExpiration });
return config;
}
public List<RouteWeb> SelRoutesWeb()
{
var cacheRoutesWebTenant = Names.CacheRoutesWeb + _serviceTenant.GetId();
var routesWebList = _memoryCache.Get<List<RouteWeb>>(cacheRoutesWebTenant);
if (routesWebList != null) return routesWebList;
routesWebList = _unitOfWork.PageWeb.SelRoutesWeb();
_memoryCache.Set(cacheRoutesWebTenant, routesWebList, new MemoryCacheEntryOptions() { SlidingExpiration = Names.CacheExpiration });
return routesWebList;
}
}
問題は、複数のタブを開いてテストし、すべてを同時に更新しようとすると、「前の操作が完了する前にこのコンテキストで2番目の操作が開始された」というメッセージが表示されることです。
私が間違っていることは確かにありますが、何が起こるかわかりません。カスタムルートミドルウェア内のdbにアクセスするより良い方法、またはこれを行うより良い方法である必要があります。
たとえば、通常のミドルウェア(ルーティングではなく)で、Invoke関数に依存関係を注入できますが、ここでRouteAsyncまたはGetVirtualPathに依存関係を注入することはできません。
ここで何が起きているのでしょうか?
前もって感謝します。
更新これらは私が得ている例外です。関連すると思います...
An unhandled exception occurred while processing the request.
InvalidOperationException: A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.
Microsoft.EntityFrameworkCore.Internal.ConcurrencyDetector.EnterCriticalSection()
Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider+ExceptionInterceptor+EnumeratorExceptionInterceptor.MoveNext()
System.Collections.Generic.List.AddEnumerable(IEnumerable<T> enumerable)
System.Linq.Enumerable.ToList<TSource>(IEnumerable<TSource> source)
MyProject.Repository.Repositories.PageWebRepository.SelRoutesWeb() in PageWebRepository.cs
+
return Context.PagesWebTrs.Include(p => p.PageWeb).Where(p => p.PageWeb.Active)
MyProject.Web.AppControl.ServiceConfig.SelRoutesWeb() in ServiceConfig.cs
+
routesWebList = _unitOfWork.PageWeb.SelRoutesWeb();
MyProject.Web.AppConfig.RouteCustom.GetVirtualPath(VirtualPathContext context) in RouteCustom.cs
+
var routeWeb = _serviceConfig.SelRoutesWeb().FirstOrDefault(p => p.Area == routeInfo.Area && p.Controller == routeInfo.Controller && p.Action == routeInfo.Action && p.LanguageCode == routeInfo.Culture);
Microsoft.AspNetCore.Routing.RouteCollection.GetVirtualPath(VirtualPathContext context, List<IRouter> routes)
Microsoft.AspNetCore.Routing.RouteCollection.GetVirtualPath(VirtualPathContext context)
Microsoft.AspNetCore.Mvc.Routing.UrlHelper.GetVirtualPathData(string routeName, RouteValueDictionary values)
Microsoft.AspNetCore.Mvc.Routing.UrlHelper.Action(UrlActionContext actionContext)
Microsoft.AspNetCore.Mvc.UrlHelperExtensions.Action(IUrlHelper helper, string action, string controller, object values, string protocol, string Host, string fragment)
Microsoft.AspNetCore.Mvc.UrlHelperExtensions.Action(IUrlHelper helper, string action, string controller, object values)
Microsoft.AspNetCore.Mvc.ViewFeatures.DefaultHtmlGenerator.GenerateForm(ViewContext viewContext, string actionName, string controllerName, object routeValues, string method, object htmlAttributes)
Microsoft.AspNetCore.Mvc.ViewFeatures.DefaultHtmlGeneratorExtensions.GenerateForm(IHtmlGenerator generator, ViewContext viewContext, string actionName, string controllerName, string fragment, object routeValues, string method, object htmlAttributes)
Microsoft.AspNetCore.Mvc.TagHelpers.FormTagHelper.Process(TagHelperContext context, TagHelperOutput output)
Microsoft.AspNetCore.Razor.TagHelpers.TagHelper.ProcessAsync(TagHelperContext context, TagHelperOutput output)
Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperRunner+<RunAsync>d__0.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
AspNetCore._Views_Contents_NewsList_cshtml+<ExecuteAsync>d__21.MoveNext() in NewsList.cshtml
+
<h1>@Model.BasePage.PageTitle</h1>
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Mvc.Razor.RazorView+<RenderPageCoreAsync>d__16.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Mvc.Razor.RazorView+<RenderPageAsync>d__15.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
System.Runtime.CompilerServices.TaskAwaiter.GetResult()
Microsoft.AspNetCore.Mvc.Razor.RazorView+<RenderAsync>d__14.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor+<ExecuteAsync>d__22.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor+<ExecuteAsync>d__21.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Mvc.ViewResult+<ExecuteResultAsync>d__26.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker+<InvokeResultAsync>d__19.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker+<InvokeNextResultFilterAsync>d__24.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResultExecutedContext context)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker+<InvokeNextResourceFilter>d__22.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker+<InvokeFilterPipelineAsync>d__17.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker+<InvokeAsync>d__15.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Builder.RouterMiddleware+<Invoke>d__4.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Authentication.AuthenticationMiddleware+<Invoke>d__6.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
MyProject.Web.AppConfig.TenantMiddleware+<Invoke>d__3.MoveNext() in Tenant.cs
+
await _next.Invoke(httpContext);
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware+<Invoke>d__7.MoveNext()
そしてこれは:
An unhandled exception occurred while processing the request.
MySqlException: There is already an open DataReader associated with this Connection which must be closed first.
MySql.Data.MySqlClient.Interceptors.ExceptionInterceptor.Throw(Exception exception)
Stack Query Cookies Headers
MySqlException: There is already an open DataReader associated with this Connection which must be closed first.
MySql.Data.MySqlClient.Interceptors.ExceptionInterceptor.Throw(Exception exception)
MySql.Data.MySqlClient.MySqlCommand.Throw(Exception ex)
MySql.Data.MySqlClient.MySqlCommand.ExecuteReader(CommandBehavior behavior)
System.Data.Common.DbCommand.ExecuteReader()
Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.Execute(IRelationalConnection connection, DbCommandMethod executeMethod, IReadOnlyDictionary<string, object> parameterValues)
Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.ExecuteReader(IRelationalConnection connection, IReadOnlyDictionary<string, object> parameterValues)
Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable+Enumerator.BufferlessMoveNext(bool buffer)
Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable+Enumerator.MoveNext()
Microsoft.EntityFrameworkCore.Query.QueryMethodProvider.GetResult<TResult>(IEnumerable<ValueBuffer> valueBuffers, bool throwOnNullResult)
lambda_method(Closure , QueryContext )
Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler+<>c__DisplayClass17_1.<CompileQueryCore>b__0(QueryContext qc)
System.Linq.Queryable.Count<TSource>(IQueryable<TSource> source)
X.PagedList.PagedList..ctor(IQueryable<T> superset, int pageNumber, int pageSize)
X.PagedList.PagedListExtensions.ToPagedList<T>(IEnumerable<T> superset, int pageNumber, int pageSize)
MyProject.Repository.Repositories.LinkRepository.SelSearchWeb(string searchText, int pageNumber, int pageSize) in LinkRepository.cs
+
return Context.LinksTrs.Include(p => p.Link)
MyProject.Web.Controllers.ContentsController.LinksList(LinksSearchModelWeb searchModel) in ContentsController.cs
+
var items = _unitOfWork.Link.SelSearchWeb(searchModel.SearchText, searchModel.PageNumber ?? 1, searchModel.PageSize ?? 10);
lambda_method(Closure , object , Object[] )
Microsoft.Extensions.Internal.ObjectMethodExecutor.Execute(object target, Object[] parameters)
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker+<InvokeActionMethodAsync>d__12.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker+<InvokeNextActionFilterAsync>d__10.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context)
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker+<InvokeInnerFilterAsync>d__14.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker+<InvokeNextResourceFilter>d__22.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker+<InvokeFilterPipelineAsync>d__17.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker+<InvokeAsync>d__15.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Builder.RouterMiddleware+<Invoke>d__4.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Authentication.AuthenticationMiddleware+<Invoke>d__6.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
MyProject.Web.AppConfig.TenantMiddleware+<Invoke>d__3.MoveNext() in Tenant.cs
+
await _next.Invoke(httpContext);
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware+<Invoke>d__7.MoveNext()
これはUnitOfWorkです
public interface IUnitOfWork : IDisposable
{
ICompanyRepository Company { get; }
IConfigRepository Config { get; }
...
void Complete();
}
public class UnitOfWork : IUnitOfWork
{
private readonly DbContext _context;
public UnitOfWork(DbContext context)
{
_context = context;
Company = new CompanyRepository(_context);
Config = new ConfigRepository(_context);
...
}
public ICompanyRepository Company { get; private set; }
public IConfigRepository Config { get; private set; }
...
public void Complete()
{
_context.SaveChanges();
}
public void Dispose()
{
_context.Dispose();
}
}
更新
コメントを確認し、多くのテストを行った後、CustomRoute行を削除すると問題が消えるので、私が持っている最良の手がかりが得られます。 Startup.csのConfigure関数からこの行を削除します
routes.Routes.Add(new RouteCustom(routes.DefaultHandler));
また、最初にRouteAsyncメソッドを削除してからGetVirtualPathメソッドを削除しようとしましたが、これらのいずれかが存在する場合はエラーが発生するため、このCustomRouteクラスに問題があることは明らかです。
リクエストに対して最初に呼び出されるTenantMiddlewareでは、UnitOfWorkを注入していますが、問題はありません。このミドルウェアは、構成機能で作成されます。
app.UseMiddleware<TenantMiddleware>();
内部では、次のようにUnitOfWorkを注入し、すべてのリクエストでそれを使用しています。
public async Task Invoke(HttpContext httpContext、IServiceTenant serviceTenant){...テナントのデータを取得するDB操作を実行します。
}
public class ServiceTenant : IServiceTenant
{
public ServiceTenant(IHttpContextAccessor contextAccessor, IMemoryCache memoryCache, IUnitOfWorkMaster unitOfWorkMaster)
{
_unitOfWorkMaster = unitOfWorkMaster;
}
...performing DB operations
}
したがって、CustomROuteの問題は、次のようにInvoke関数に追加して依存関係を挿入できないことです。
public async Task Invoke(HttpContext httpContext, IServiceTenant serviceTenant)
したがって、対応するサービス(そのサービス内でUnitOfWorkを挿入し、DB操作を実行する)を呼び出す必要があり、これが問題を引き起こしている可能性があると思います。
public async Task RouteAsync(RouteContext context)
{
_serviceConfig = context.HttpContext.RequestServices.GetRequiredService<IServiceConfig>();
....
}
これが、IServiceConfigをRouteAsyncとGetVirtualPathに「注入」する唯一の方法だからです...
また、BaseCOntrollerを使用しているので、すべてのコントローラーでそれを行っています。そのため、使用する注入サービスを決定します。
public class BaseWebController : Controller
{
private readonly IMemoryCache _memoryCache;
private readonly IUnitOfWork _unitOfWork;
private readonly IUnitOfWorkMaster _unitOfWorkMaster;
private readonly IServiceConfig _serviceConfig;
private readonly IServiceFiles _serviceFiles;
private readonly IServiceFilesData _serviceFilesData;
private readonly IServiceTenant _serviceTenant;
public BaseWebController(IServiceProvider serviceProvider)
{
_memoryCache = serviceProvider.GetRequiredService<IMemoryCache>();
_unitOfWork = serviceProvider.GetRequiredService<IUnitOfWork>();
_unitOfWorkMaster = serviceProvider.GetRequiredService<IUnitOfWorkMaster>();
_serviceConfig = serviceProvider.GetRequiredService<IServiceConfig>();
_serviceFiles = serviceProvider.GetRequiredService<IServiceFiles>();
_serviceFilesData = serviceProvider.GetRequiredService<IServiceFilesData>();
_serviceTenant = serviceProvider.GetRequiredService<IServiceTenant>(); }
...
}
そして、すべてのコントローラーで、注入されたすべてのサービスを参照するのではなく、次のように必要なサービスに対してのみそれを行うことができます:
public class HomeController : BaseWebController
{
private readonly IUnitOfWork _unitOfWork;
public HomeController(IServiceProvider serviceProvider) : base(serviceProvider)
{
_unitOfWork = serviceProvider.GetRequiredService<IUnitOfWork>();
}
public IActionResult Index()
{
...
}
}
これが私の問題と関係があるかどうかはわかりませんが、問題になりそうなことを示しているだけなので、より多くの情報を得ることができます。
ありがとう。
更新
これは、ルートを取得するdbのコードです。
public class PageWebRepository : Repository<PageWeb>, IPageWebRepository
{
public PageWebRepository(DbContext context) : base(context) { }
public List<RouteWeb> SelRoutesWeb()
{
return Context.PagesWebTrs.Include(p => p.PageWeb).Where(p => p.PageWeb.Active)
.Select(p => new RouteWeb
{
PageWebId = p.PageWebId,
LanguageCode = p.LanguageCode,
Route = p.Route,
Regex = p.PageWeb.Regex.Replace("<route>", p.Route),
Params = p.PageWeb.Params,
Area = p.PageWeb.Area,
Controller = p.PageWeb.Controller,
Action = p.PageWeb.Action,
Type = p.PageWeb.Type,
Sidebar = p.PageWeb.Sidebar,
BannerIsScript = p.PageWeb.BannerIsScript,
Title = p.Title,
Description = p.Description,
Keywords = p.Keywords,
ScriptHead = p.ScriptHead,
ScriptBody = p.ScriptBody,
BannerScript = p.BannerScript,
BannerUrl = p.BannerUrl,
}).ToList();
}
}
PagesWebTrsはページの翻訳(多言語)であり、PagesWebはメインテーブルです。
実際、この問題はルートミドルウェア内にあります。
定義により、ミドルウェアはシングルトンであるため、単一のインスタンスがすべてのリクエストを処理します。これにより、インスタンス状態(the IServiceConfig
withフックアップDbContext
)複数の同時リクエストによるアクセスと変更。それはよく隠された古典的な並行性の問題です。
例。
リクエストAはRouteAsync
を実行し、_serviceConfig
とDbContext
に対してクエリを実行します。ナノ秒(またはそれ以下:))後、リクエストBは同じことをします。リクエストBのクエリが実行されている間、リクエストAはGetVirtualPath
を実行しますが、今回はリクエストBによって設定されたDbContext
で実行されます。これにより、DbContext
リクエストBの1つがまだ実行されており、前述のエラーが表示されます。
解決策は、各メソッドの開始時にIServiceConfig
を取得することにより、共有状態を防ぐことです。
すでに述べたように、Invoke
メソッドを介してこのような依存関係を挿入しても機能しません。Invoke
methodは実行されません。
以下は、再加工されたRouteCustom
です。
public class RouteCustom : IRouteCustom
{
private readonly IRouter _innerRouter;
public RouteCustom(IRouter innerRouter)
{
_innerRouter = innerRouter ?? throw new ArgumentNullException(nameof(innerRouter));
}
public async Task RouteAsync(RouteContext context)
{
var serviceConfig = context.HttpContext.RequestServices.GetRequiredService<IServiceConfig>();
// ...
}
public VirtualPathData GetVirtualPath(VirtualPathContext context)
{
var serviceConfig = context.HttpContext.RequestServices.GetRequiredService<IServiceConfig>();
// ...
}
}