Skip to content
This repository was archived by the owner on Dec 14, 2018. It is now read-only.
This repository was archived by the owner on Dec 14, 2018. It is now read-only.

Filter caching is too aggressive #4616

Closed
@rynowak

Description

@rynowak

We're mistakenly keeping references to filters even when they say they aren't reusable. This creates all kinds of use-after-dispose problems, and will break apps that worked in RC1.

The bug is here: https://github.com/aspnet/Mvc/blob/dev/src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllerActionInvokerCache.cs#L59

The GetFilters method is too granular, the same logic can't work for the 'first time' and for the cached case.

To repro this add a filter like:

    public class MyFilter : IActionFilter, IDisposable
    {
        private bool _isDisposed;
        private ILogger<MyFilter> _logger;

        public MyFilter(ILogger<MyFilter> logger)
        {
            _logger = logger;

            _logger.LogError($"Created {RuntimeHelpers.GetHashCode(this)}");
        }

        public void Dispose()
        {
            _isDisposed = true;
            _logger.LogError($"Disposed {RuntimeHelpers.GetHashCode(this)}");

        }

        public void OnActionExecuted(ActionExecutedContext context)
        {
        }

        public void OnActionExecuting(ActionExecutingContext context)
        {
            _logger.LogError($"Executing {RuntimeHelpers.GetHashCode(this)}");
            if (_isDisposed)
            {
                _logger.LogError($"Use after dispose {RuntimeHelpers.GetHashCode(this)}");
                throw new Exception();
            }
        }
    }

Register it as a scoped service: services.AddScoped<MyFilter, MyFilter>();

Add it as a ServiceFilter:

        [ServiceFilter(typeof(MyFilter))]
        public IActionResult Index()
        {
            return View();
        }

You'll see an exception on the third request.

Output is like

info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 GET http://localhost:5000/
fail: MvcSandbox.MyFilter[0]
      Created 25517075
fail: MvcSandbox.MyFilter[0]
      Executing 25517075
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
      Executing action method MvcSandbox.Controllers.HomeController.Index (MvcSandbox) with arguments () - ModelState is Valid'
info: Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.ViewResultExecutor[1]
      Executing ViewResult, running view at path /Views/Home/Index.cshtml.
info: Microsoft.Extensions.DependencyInjection.DataProtectionServices[0]
      User profile is available. Using 'C:\Users\rynowak\AppData\Local\ASP.NET\DataProtection-Keys' as key repository and Windows DPAPI to encrypt keys at rest.
info: Microsoft.AspNetCore.Mvc.Internal.MvcRouteHandler[2]
      Executed action MvcSandbox.Controllers.HomeController.Index (MvcSandbox) in 6722.2956ms
fail: MvcSandbox.MyFilter[0]
      Disposed 25517075
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
      Request finished in 7122.4393ms 200 text/html; charset=utf-8
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 GET http://localhost:5000/
fail: MvcSandbox.MyFilter[0]
      Created 48094426
fail: MvcSandbox.MyFilter[0]
      Executing 48094426
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
      Executing action method MvcSandbox.Controllers.HomeController.Index (MvcSandbox) with arguments () - ModelState is Valid'
info: Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.ViewResultExecutor[1]
      Executing ViewResult, running view at path /Views/Home/Index.cshtml.
info: Microsoft.AspNetCore.Mvc.Internal.MvcRouteHandler[2]
      Executed action MvcSandbox.Controllers.HomeController.Index (MvcSandbox) in 4.4185ms
fail: MvcSandbox.MyFilter[0]
      Disposed 48094426
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
      Request finished in 5.8475ms 200 text/html; charset=utf-8
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 GET http://localhost:5000/
fail: MvcSandbox.MyFilter[0]
      Executing 48094426
fail: MvcSandbox.MyFilter[0]
      Use after dispose 48094426

Notice that the object with ID 48094426 gets used twice

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions