Is there a way to pass an instance of DbContext from the controller to an ActionFilter?

2.2k views Asked by At

I have an application that is written using c# on the top of the ASP.NET MVC 5 framework.

I created a custom action filter (i.e. ValidateCookieValueAction) by inheriting the ActionFilterAttribute class. I am using ValidateCookieValueAction attribute for multiple of my action methods.

The filter's purpose is to make sure a user has a cookie value before allowing them in to the action. Despite the security concern, the filter works great. However, the cookie value itself need to be validated before the user is let in.

To validate the cookie value, I need an instance of the DbContext so I can query the database and validate the cookie value.

I know I can create an new instance of DbContext directly inside the ActionFilter. However, I want to avid that. I want to be able to pass the DbContext instance that I already created from the controller to allow me to reuse the connection that I already have established in the controller.

Here are how my controllers setup

public class BaseController 
{
    protected IDbContext Context { get; private set; }

    protected override void Initialize(RequestContext requestContext)
    {
        base.Initialize(requestContext);

        Context = new DbContext();
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            Context.Dispose();
        }

        base.Dispose(disposing);
    }
}

public class TestController : BaseController 
{
    [ValidateCookieValueAction]
    public ActionResult Index()
    {
        // the uses is in!
        return View();
    }
}

Here is my action filter class

public class ValidateCookieValueAction : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        base.OnActionExecuting(filterContext);

        var cookie = new CookieJar(filterContext.HttpContext);
        if (!cookie.Has(ContactListCookieName))
        {
            var Url = new UrlHelper(filterContext.RequestContext);
            var url = Url.Action("Guest", "Test");

            filterContext.Result = new RedirectResult(url);
        }
    }
}

How can I pass instance of my Context to the ValidateCookieValueAction?

2

There are 2 answers

4
Win On BEST ANSWER

This is not directly related to original question, but it seems like you are create an authentication mechanism by yourself. If not, please let me know; I'll just delete this answer.

ASP.NET already has ASP.NET Identity which is more secure and more reliable than create it by ourselves. However, if you want use your existing custom tables, you can use OWIN Middleware which basically is a subset of ASP.NET Identity.

The primary advantage of using OWIN Middleware is that you could use Authorize attribute come with ASP.NET MVC. Implementation is a lot easier than you think.

Sample code -

OWIN Authentication Middle-ware

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = "ApplicationCookie",
            LoginPath = new PathString("/Account/Login")
        });
    }
}

Store access as role claim in Principle object

public void SignIn(User user, IList<string> roleNames)
{
    IList<Claim> claims = new List<Claim>
    {
        new Claim(ClaimTypes.Sid, user.Id.ToString()),
        new Claim(ClaimTypes.Name, user.UserName),
        new Claim(ClaimTypes.GivenName, user.FirstName),
        new Claim(ClaimTypes.Surname, user.LastName),
    };

    foreach (string roleName in roleNames)
    {
        claims.Add(new Claim(ClaimTypes.Role, roleName));
    }

    ClaimsIdentity identity = new ClaimsIdentity(claims, AuthenticationType);

    IOwinContext context = _context.Request.GetOwinContext();
    IAuthenticationManager authenticationManager = context.Authentication;

    authenticationManager.SignIn(identity);
}

Usage

[Authorize(Roles = "CanViewHome")]
public class IndexController : Controller
{
    [Authorize(Roles = "CanEditHome")]
    public ActionResult Edit()
    {
        return View();
    }
}
0
AudioBubble On

there is already a context stored in the current context by default.

in Startup.Configuration(IAppBuilder app)

app.CreatePerOwinContext(ApplicationDbContext.Create); // this already stores a DbContext

you can fetch it with

var dbContext = HttpContext.GetOwinContext().Get<ApplicationDbContext>();

Alternatively you can store the Context in the [TempData] or [Session].
If you refetch it in the same Request, then just throw the DbContext in the Context.Items[], these only last for the current Request.

in OnActionExecuting:

filterContext.Controller.TempData

in the Controller:

this.TempData