views:

38

answers:

2

I've got a thread that sends emails around. I need to generate ActionLinks as part of the content of the email so the user can click on the link and be redirected to the website, exactly to the required page. I tried to instantiate a UrlHelper class and use it's Action method to generate the link but since threads don't run within the context of any request I get exceptions at the time of generating the ActionLink.

How can I do this?

A: 

You can give the thread access to an existing UrlHelper by passing it to the thread starter. If your thread is started from a controller, just pass the UrlHelper in the controller's Url property:

new Thread(
    urlHelper =>
    {
        var url = 
            ((UrlHelper)urlHelper)
            .Action("Index", "Home", new { Id = 5 });
        // use url here
    }
).Start(Url);
bzlm
Good answer, but my thread is created at the application start.
Am
A: 

You need to fake HttpContextBase and pass this to an UrlHelper which you can use in a thread without an HttpContext. Here is the rough idea, although you will need to create a class around it etc, this is a quick proof of concept as unit tests don't have an HttpContext either.

[TestFixture]
public class RouteTestClass
{
    private UrlHelper helper;

    public RouteTestClass()
    {
        MvcApplication.RegisterRoutes(RouteTable.Routes); //You dont need to do this if its done in global.asax!
        var c = new RequestContext(new FakeContext(), new RouteData());
        helper = new UrlHelper(c, RouteTable.Routes);
    }

    [Test]
    public void TestGetHomeIndex()
    {
        var url = helper.Action("Index", "Home");
        Assert.AreEqual("/",url);
    }
}

public class FakeContext : HttpContextBase
{
    public override HttpRequestBase Request { get { return new FakeRequest(); } }
    public override HttpResponseBase Response { get { return new FakeResponse(); } }
}

public class FakeRequest : HttpRequestBase
{
    public override string ApplicationPath { get { return "/"; } }
    public override NameValueCollection ServerVariables { get { return new NameValueCollection(); } }
}

public class FakeResponse : HttpResponseBase
{
    public override string ApplyAppPathModifier(string virtualPath)
    {
        return virtualPath;
    }
}

Edit

Looking at this answer, I tidied the code up a little as I don't need to create fakes for HttpRequestBase and HttpResponseBase myself.

[TestFixture]
public class RouteTestClass
{
    private UrlHelper helper;

    public RouteTestClass()
    {
        MvcApplication.RegisterRoutes(RouteTable.Routes);
        var req = new HttpRequest("/", "http://www.yoururl.com", "");
        var resp = new HttpResponse(new StringWriter());
        var httpContext = new HttpContext(req, resp);
        var c = new RequestContext(new HttpContextWrapper(httpContext), new RouteData());
        helper = new UrlHelper(c, RouteTable.Routes);
    }

    [Test]
    public void TestGetHomeIndex()
    {
        var url = helper.Action("Index", "Home");
        Assert.AreEqual("/",url);
    }
}
amarsuperstar
Thats a kickass answer. I'll try it thanks
Am