1

I need to run some c# code each time any page based on _layout.cshtml is viewed. I don't want to put something in every controller.cs file, just something central like you used to have in ASP.NET's MasterPage.cs

Can't get this

Run a method in each request in MVC, C#?

or this

@Html.Action in Asp.Net Core

to run, not sure if it's because they're not CORE 2.0.0, I just get a lot of compilation errors. All I want to do is be able to run some code like this

public class myClass {
    public static bool returnTrue() {
        return true;
    }
}

every time each page is loaded.

RonC
  • 31,330
  • 19
  • 94
  • 139

2 Answers2

7

You can accomplish this with an action filter

  public class GlobalFilter : IActionFilter{

         public void OnActionExecuting(ActionExecutingContext context) {
             //code here runs before the action method executes
         }

         public void OnActionExecuted(ActionExecutedContext context) {
              //code here runs after the action method executes
         }
  }

Then in the Startup.cs file in the ConfigureServices method you wire up the ActionFilter like so:

services.AddScoped<GlobalFilter>();                //add it to IoC container.
services.AddMvc().AddMvcOptions(options => {
     options.Filters.AddService(typeof(GlobalFilter));  //Tell MVC about it
 });

Then you can place code in this ActionFilter which can run before every action method and you can place code in it to run after every action method. See code comments.

Through the context parameter you have access to the Controller, Controller Name, Action Descriptor, Action Name, Request object (Including the path) and so on, so there is lots of info available for you to determine which page you want to execute the code for. I'm not aware of a specific property that will tell you if the page is using _layout.cshtml but you could probably deduce that based on the other properties I mentioned.

Enjoy.

RonC
  • 31,330
  • 19
  • 94
  • 139
  • Where've you been the last three days of my life :) That's got it thanks. All my pages will be using _layout.cshtml I was basing my question on how I know ASPX works as I'd now idea where to start looking how to do it in MVC. –  Nov 21 '17 at 15:49
  • Glad to be of help. – RonC Nov 21 '17 at 15:59
2

Filter would also work, but the correct way to go in .Net Core is Middleware. You can read more about it here.

If it's something simple as your example, you can go with the first examples on the link like:

app.Use(async (context, next) =>
        {
            returnTrue();
            await next.Invoke();
        });

Let me know if it helped!

João Pereira
  • 1,647
  • 15
  • 18
  • That's only run once at app start up as it stands. I looked into the per-request as it says at the bottom but that's just been tagged on with no way I can see to link it to the rest of the page. I've tried looking for guidance on per-request but the websites I can find just regurgitate what's on that page and add more random things with no indication of how to put them all together to make something that works –  Nov 21 '17 at 12:30
  • No it doesn't. It runs on every server request. Just spin a new project, copy paste the code before the app.UseMvc statement and put a breakpoint inside it. – João Pereira Nov 21 '17 at 17:07
  • The big difference between Middleware and Filter in .NetCore is that Middleware gets executed earlier when the pipeline is being built to handle request, while ActionFilter gets executed later when entering the Action on the Controller. Middleware can stop the request or redirect before everything is setup to handle the request. Since you mentioned Global.asax, the most equivalent in NetCore is Middleware and not ActionFilters. – João Pereira Nov 21 '17 at 17:11
  • It's a new, empty project I've been using to try all this out, with no success. The page you linked to says "Per-request dependencies Because middleware is constructed at app startup, not per-request, scoped lifetime services used by middleware constructors are not shared with other dependency-injected types during each request" –  Nov 21 '17 at 18:11
  • Are you putting the code **BEFORE** before the `app.UseMvc` like I stated? I'm pretty sure it does work!! The reference from the website that you wrote refers to Middleware that is written on a difference class, that needs to be registered. If you use `Use`, `Run`, `Map` and `MapWhen` you are registering the middleware to execute the code inside it **on every request**! – João Pereira Nov 22 '17 at 00:22
  • The definition of Middleware on the link says it all: "Middleware is software that application assembles into the pipeline to **handle requests and responses**. **Each part chooses whether to pass the request on to the next part in the pipeline, and can do certain actions before and after application invokes the next part in the pipeline.** Request delegates usage is to build the request pipeline." – João Pereira Nov 22 '17 at 00:25
  • Perhaps this is more explicitly explained: [Middleware in ASP.NET Core – Handling requests](https://codingblast.com/asp-net-core-middleware/). This sentence says it all: "After initialization is complete, our app is ready to process requests. Once a request hits our server, it is middleware that takes over and handles it.". **Middleware gets executed before filters do!** – João Pereira Nov 22 '17 at 00:27
  • I am not saying that the answer from Ron C is wrong! It also works, but at a later stage of the request. Middleware and filters have different usages. For example, if you execute an action that does not exist, filters aren't executed and a 404 is returned. But with middleware, even if the action doesn't exist, it gets executed every single time, meaning that you can even hijack the request and redirect the request to a custom url. – João Pereira Nov 22 '17 at 00:32
  • I mentioned Middleware because as for your example, it seems more appropriate to use Middleware. But perhaps for your real use case Filters are more appropriate. Your call! – João Pereira Nov 22 '17 at 00:34