Search code examples
c#asp.net-mvchttpcontexturlhelper

MVC UrlHelper called from Service


I have a static class that sends emails with links to certain pages of my site. That link is getting dynamically generated with this code:

UrlHelper urlHelper = new UrlHelper(HttpContext.Current.Request.RequestContext);
string url = urlHelper.Action("Details", "Product", new { id = ticketId }, "http");

The problem is I now also have a service that periodically compares the creation date with the current date and sends those mails automatically. The code crashes of course and says HttpContext.Current is null (because it ain't a request).

I tried some things like that:

private static System.Web.Routing.RequestContext requestContext;
private static System.Web.Routing.RequestContext RequestContext {
    get
    {
        if(requestContext == null)
            requestContext = HttpContext.Current.Request.RequestContext;
        return requestContext;
    }
}

But when I need RequestContext the second time for the UrlHelper.Action crashes saying Null Reference Exception.

I failed to somehow save/remember/pass the UrlHelper or the HttpContext to have access when calling the mail method over my service.


Solution

  • Thanks for all your help. Predefining the URL was no option in my case. I found more or less a solution for my problem. I know it might not be the most beautiful code but it works quite well and noone seems to have a better one, so no downvotes please.

    In global.asax.cs I added this class:

    class FirstRequestInitialisation
    {
        private static string host = null;
    
        private static Object s_lock = new Object();
    
        // Initialise only on the first request
        public static void Initialise(HttpContext context)
        {
            if (string.IsNullOrEmpty(host))
            { //host isn't set so this is first request
                lock (s_lock)
                { //no race condition
                    if (string.IsNullOrEmpty(host))
                    {
                        Uri uri = HttpContext.Current.Request.Url;
                        host = uri.Scheme + Uri.SchemeDelimiter + uri.Host + ":" + uri.Port;
    
                        //open EscalationThread class constructor that starts anonymous thread that keeps running. 
                        //Constructor saves host into a property to remember and use it.
                        EscalationThread et = new EscalationThread(host);
                    }
                }
            }
        }
    }
    

    And I added this:

    void Application_BeginRequest(Object source, EventArgs e)
    {
        FirstRequestInitialisation.Initialise(((HttpApplication)source).Context);
    }
    

    Explaination what happens: On each and every request the FirstRequestInitialisation class gets called with the method Initialise with the context as parameter. This is never a problem because the context is known in a Application_BeginRequest (not like in Application_Start). The Initialise Method takes care that still the Thread is just called once and has a lock so that it will never crash. I eliminated my Service because I can't really communicate with it and instead I decided to make a Thread out of it. In this Initialise Method I call a class constructor EscalationThread with the host as parameter. In this constructor I create and start the thread that just keeps running.

    I still don't have HttpContext and can't use the UrlHelper BUT I have the host and can do stuff like: string urlInMail = this.host + string.Format("/{0}/{1}/{2}", "Product", "Details", product.Id);