2

I want to create connection between global.asax and my controller. We are using a database in our website. We have a code to check whether the database exists or not. If it doesn't not exist, we want to display a message like "database initializing" etc..

Database check in GLOBAL.ASAX :

public class MvcApplication : System.Web.HttpApplication
{
    public static bool flag;
    protected void Application_Start()
    {
        Database.SetInitializer<PhoneDexContext>(null);

        var connString = ConfigurationManager.ConnectionStrings["DatabaseConnection"].ConnectionString;

        using (PhoneDexContext db = new PhoneDexContext())
        {
            if (!db.Database.Exists())
            {
                flag = true;
                db.Database.CreateIfNotExists();              
            }

        }

        AreaRegistration.RegisterAllAreas();
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
    }

    protected void Application_BeginRequest()
    {
        if (flag)
        {
            Response.Redirect("Loading/LoadingScreen");
        }
    }
}

But we have a problem with using response redirect. It returns

{"Response is not available in this context."}.

We have a controller which is LoadingController and it has this code;

public class LoadingController : Controller
{
    // GET: Loading
    public ActionResult LoadingScreen()
    {

        return View();
    }
}

But we can't jump to this part. How can i make connection?? Thanks

Berkin
  • 1,565
  • 5
  • 22
  • 48
  • 1
    where are you execution this database check code ? which part of MVC request pipeline ? – Shyju Aug 22 '16 at 11:50
  • in global.asax. We dont know request pipeline – Berkin Aug 22 '16 at 11:51
  • 1
    Request and Response are not available at startup. Create an action filter and have it do the db check then redirect as needed – Nkosi Aug 22 '16 at 11:57
  • You can set a Session value to false, then check for that at the Home controller, default method – Felipe Deguchi Aug 22 '16 at 11:58
  • Hmm, but i want to display an alert to user if database not ready, wait for loading. How can i do it? I think i should do in global asax – Berkin Aug 22 '16 at 11:58
  • I think you can not do this because this code could also be executed without a user. The code gets executed when for example the application pool gets recycled. – Michel Aug 22 '16 at 12:04
  • @Berkin you need to go further down the pipeline before you have access to the request and response. You cannot do what you are requesting at startup – Nkosi Aug 22 '16 at 12:06

6 Answers6

3

First, Application_Start does not handle any user requests. It is just perform some start up initialization. It invoked only once when app starts. To do some checks based on user's actions and properly respond you need to move these checks into Application_BeginRequest method.

Second, you also need to check if user already requesting /Loading/LoadScreen before responding with redirect to that page. Otherwise you will get an infinite redirects until database created.

public class MvcApplication : HttpApplication
{
    private static bool dbInitialized;

    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);

        // We can do it asynchronously to not block other initialization code.
        Task.Run((Action)CreateDataBase);
    }

    private static void CreateDataBase()
    {
        Database.SetInitializer<PhoneDexContext>(null);

        using (PhoneDexContext db = new PhoneDexContext())
        {
            if (!db.Database.Exists())
                db.Database.CreateIfNotExists();
        }

        dbInitialized = true;
    }

    protected void Application_BeginRequest()
    {
        if (!dbInitialized && !this.Request.Url.LocalPath.StartsWith("/Loading/LoadingScreen", StringComparison.OrdinalIgnoreCase))
        {
            this.Response.Redirect("/Loading/LoadingScreen");
        }
    }
}

You can to further and move checks into the ActionFilter as you will able to work with RouteData and check action and controller parameters instead of url. Combined with nameof that would be less error-prone to route changes, renaming, refactoring and so on.

lorond
  • 3,856
  • 2
  • 37
  • 52
0

I think this answer will give you what you want: Return different views in a controller

In your case instead of the response.redirect use return View("/Loading/LoadScreen"); ...or something simular

Community
  • 1
  • 1
0

try this

HttpContext.Current.Response.Redirect("/Loading/LoadScreen");

beacuse Response object is not avaliable in application_start method of global.asax. you must use HttpContext.Current for any situation.

  • Should i move createIfNotExist part to begin request? Can you write the code? I will edit my first code please check it again – Berkin Aug 22 '16 at 12:33
  • can you write db.Database.CreateIfNotExists(); before HttpContext.Current.Response.Redirect("/Loading/LoadScreen"); it will work fine. – Mehmet Emin Yalçın Aug 22 '16 at 12:35
0

Application_Start happens before ASP.Net starts processing the request.

You could set a global static flag to indicate the error condition, then handle Application_BeginRequest and check the flag and redirect.

static bool _isDbLoaded;
public class MvcApplication : System.Web.HttpApplication
{ 
    protected void Application_Start(){
        Database.SetInitializer<PhoneDexContext>(null);

        var connString = ConfigurationManager.ConnectionStrings["DatabaseConnection"].ConnectionString;

        using (PhoneDexContext db = new PhoneDexContext())
        {
            if (!db.Database.Exists())
            {
                _isDbLoaded = false;
                db.Database.CreateIfNotExists();
             }
         }
        AreaRegistration.RegisterAllAreas();
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
   }
}

protected void Application_BeginRequest(){
    if(!_isDbLoaded){
        Response.Redirect("Loading/LoadingPage");
    }
}
Felipe Deguchi
  • 586
  • 1
  • 7
  • 25
  • it enters infinite loop i guess :(. Its a a loop but i dont know is it infinite or not – Berkin Aug 22 '16 at 12:11
  • I'll edit soon. Suposed to check if current page is the redirect destination – Felipe Deguchi Aug 22 '16 at 12:14
  • since `_isDbLoaded` is only set at startup and never changing later app will always redirect to `LandingPage`. Also you do not check if user already on a landing page so user will be redirected to landing even from landing. – lorond Aug 22 '16 at 12:20
  • @lorond Good Point. He needs to set that variable to true after CreateIfNotExists. The problem is, he will never redirect to that page. It will just wait the creation, unless it's async creation – Felipe Deguchi Aug 22 '16 at 12:27
  • its infinite loop :( im sorry – Berkin Aug 22 '16 at 13:37
0

Since Request and Response won't be available in your Application_Start event, You might consider having this code somewhere in the MVC request-response pipeline. An action filter is a good place.

public class VerifySetupIsGood : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        var dbSetDate = context.HttpContext.Application["DbSetDate"] as string;
        if (String.IsNullOrEmpty(dbSetDate))
        {
           //to do : Execute your custom code to check db existence here

            var values = new Dictionary<string, string> { { "action", "LoadScreen" },
                                                             { "controller", "Loading" } };

            var r = new RouteValueDictionary(values);
            //redirect the request to MissingDatabase action method.
            context.Result = new RedirectToRouteResult(r);
        }            
        base.OnActionExecuting(context);
    }
}

Here we are first checking whether the application variable has a valid entry for "DbSetDate" key. By default, it will not be there. Then you have to execute your custom code to check whether your db exist. If not, Redirect to the LoadScreen action.

Register this filter globally so that it will be executed for any request coming to your application.

GlobalFilters.Filters.Add(new VerifySetupIsGood());

Now in when you are done with setting up your database, update this application variable to have a valid value.

HttpContext.Application["DbSetDate"] = DateTime.Now.ToString();

Remember, Application variable states will also gets reset. So don't just rely on that. You should run your custom code to check your db exists inside the if condition. If condition is to prevent your custom db check for every single request.

lorond
  • 3,856
  • 2
  • 37
  • 52
Shyju
  • 214,206
  • 104
  • 411
  • 497
  • I wouldn't execute an action for every request (which could possibly happen lots and lots of times) for something that only happens occasionally (the database update) – Michel Aug 22 '16 at 12:13
  • The first thing it checks is an application variable value. So the code inside the if condition won't be executed for all the requests. – Shyju Aug 22 '16 at 12:16
  • fair enough, but still the filter gets executed every request. My opinion is that this does not reflect the requirement which is to check db existence at application startup. – Michel Aug 22 '16 at 12:22
  • Do not forget to check if user already on `LoadScreen`. Otherwise it will redirects infinitely since filter is global. – lorond Aug 22 '16 at 12:22
0

You can also use the 'Auto-Start feature' (How to warm up an ASP.NET MVC application on IIS 7.5? : Darins answer). This will execute once, before the website is ready to serve requests. The downside is you can't show the user a 'please wait' window. Personally I would not check at every request if the database is existent or not. Just execute it; the database update should not take very long I guess?

Community
  • 1
  • 1
Michel
  • 23,085
  • 46
  • 152
  • 242