Pergunta

I have a service class that is required to create a pdf, which needs the ControllerContext injected, in order to render a the html => pdf. This service is called through a middle tier, that has no reference to the web/mvc project. which is fine, since ninject does all the required injection of services etc. This is what the Service looks like (simplified for these purposes)

public class PdfCreatorService : AbstractUrlBasedPdfCreatorService
{
    [Inject]
    public ControllerContext ControllerContext { get; set; }

    public override byte[] CreateReport(int reportId)
    {
        var result = new PdfController().CreateReport(reportId);
        using (var it = new ResponseCapture(ControllerContext.RequestContext))
        {
            result.ExecuteResult(ControllerContext);
            return it.ReadAllContents();
        }
    }
}

Here is the simplified call stack:

Web.HomeController.SendEmailWithPdf(int id) calls:
MiddleTier.BusinessLogic.SendEmailWithPdf(int id) calls:
Web.Services.PdfCreatorService.CreateReport(int id)

Ninject is reaching PdfCreatorService, no problem, but: I need to somehow forward the ControllerContext from the HomeController (through the middle tier) to the PdfCreatorService. Whilst the middle tier cannot have any reference to the ControllerContext.

I've looked at Providers, Factories, Resolver, etc. But couldn't find the right solution.

Any help is appreciated! Cheers

Foi útil?

Solução

OK. I've come up with a solution that I'm happy with.
Here is how I'm retrieving the ControllerContext:

public class PdfCreatorService 
{
    [Inject]
    public ControllerContextProvider contextProvider { get; set; }
    [Inject]
    public PdfController pdfController { get; set; }

    public override byte[] CreateReport(int reportId)
    {
        var context = contextProvider.GetControllerContext();
        using (var stream = new ResponseCapture(context.RequestContext))
        {
            // Setup Controller
            var routeData = new RouteData(context.RouteData.Route, context.RouteData.RouteHandler);
            routeData.Values.Add("action", "CreateReport");
            routeData.Values.Add("controller", "Pdf");
            routeData.Values.Add("id", reportId);
            var pdfContext = new ControllerContext(context.HttpContext, routeData, pdfController);

            // Execute Controller
            var result = pdfController.CreateReport(reportId);
            result.ExecuteResult(pdfContext);

            return stream.ReadAllContents();
        }
    }
}

This is how to set the context in the original controller:

public abstract class HomeController : Controller
{
    [Inject]
    public ControllerContextProvider ControllerContextProvider { get; set; }

    protected override IAsyncResult BeginExecute(RequestContext requestContext, AsyncCallback callback, object state)
    {
        ControllerContextProvider.GetControllerContext = () => ControllerContext;
        return base.BeginExecute(requestContext, callback, state);
    }
}

This is what the provider class looks like:

public class ControllerContextProvider
{
    public Func<ControllerContext> GetControllerContext { get; set; }
}

This is how I'm binding it:

public class PortalNinjectModule : NinjectModule
{
    public override void Load()
    {
        Bind<ControllerContextProvider>().ToSelf().InRequestScope();
    }
}

Still interested to see whether anyone has a more elegant solution.

Outras dicas

You could create a Custom Controller factory.

public class MyControllerFactory : DefaultControllerFactory
{
    public override IController CreateController(RequestContext requestContext, string controllerName)
    {
        var controller = base.CreateController(requestContext, controllerName);
        HttpContext.Current.Request["controllerInstance"] = controller;
        return controller;
    }
}

You need to register this controller in your global.asax:

ControllerBuilder.Current.SetControllerFactory(typeof(MyControllerFactory));

After that you can configure Ninject to resolve the ControllerContext from the request:

kernel.Bind<ControllerContext>().ToMethod(ctx => ((Controller)HttpContext.Current.Request["controllerInstance"]).ControllerContext);
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top