質問

First, some context:

  • Language - C#
  • Platform - .Net Framework 4.5
  • Project type - ASP.Net MVC 4

I am trying to determine which View in an MVC project is handling an explicit call to the following method. The MSDN docs for the method are here: http://msdn.microsoft.com/EN-US/library/dd492930.aspx

protected internal ViewResult View(
    Object model
)

The original Author is using a View to generate a PDF file with a third-party library. I need to modify the view to include additional information.

The problem: I'm having trouble finding which View to modify. There are hundreds of them, and (IMHO) they are poorly named and organized. The basic process for generating a PDF looks like this. I'm getting confused in between steps 3 and 4.

  1. An Entity's ID is passed to an ActionResult
  2. The Entity is retrieved from the backing store
  3. The model is passed to the Controller.View method mentioned above:
    var viewModel = View(model);
    var xmlText = RenderActionResultToString(viewModel);
  4. The resulting ViewResult is used with an instance of ControllerContext to generate HTML as if being requested by a browser.
  5. The resulting HTML is passed to the third-party tool and converted to a PDF.

I understand everything else very clearly. What I don't understand is how the call to View(model) determines which View file to use when returning the ViewResult. Any help greatly appreciated!

I'm including the code below, in case it helps anybody determine the answer.

The ActionResult:

public ActionResult ProposalPDF(String id, String location, bool hidePrices = false)
{
    var proposal = _adc.Proposal.GetByKey(int.Parse(id));

    var opportunity = _adc.Opportunity.GetByKey(proposal.FkOpportunityId.Value);
    ViewData["AccountId"] = opportunity.FkAccountId;
    ViewData["AccountType"] = opportunity.FkAccount.FkAccountTypeId;

    ViewData["Location"] = location;
    ViewData["HidePrices"] = hidePrices;
    return ViewPdf(proposal);
}

The ViewPDF method:

protected ActionResult ViewPdf(object model)
{
    // Create the iTextSharp document.
    var document = new Document(PageSize.LETTER);

    // Set the document to write to memory.
    var memoryStream = new MemoryStream();
    var pdfWriter = PdfWriter.GetInstance(document, memoryStream);
    pdfWriter.CloseStream = false;
    document.Open();

    // Render the view xml to a string, then parse that string into an XML dom.
    var viewModel = View(model);
    var xmlText = RenderActionResultToString(viewModel);

    var htmlPipelineContext = new HtmlPipelineContext();
    htmlPipelineContext.SetTagFactory(Tags.GetHtmlTagProcessorFactory());

    //CSS stuff
    var cssResolver = XMLWorkerHelper.GetInstance().GetDefaultCssResolver(false);
    var cssResolverPipeline = new CssResolverPipeline(cssResolver, new HtmlPipeline(htmlPipelineContext, new PdfWriterPipeline(document, pdfWriter)));

    var xmlWorker = new XMLWorker(cssResolverPipeline, true);
    var xmlParser = new XMLParser(xmlWorker);

    xmlParser.Parse(new StringReader(xmlText));

    // Close and get the resulted binary data.
    document.Close();

    var buffer = new byte[memoryStream.Position];
    memoryStream.Position = 0;
    memoryStream.Read(buffer, 0, buffer.Length);

    // Send the binary data to the browser.
    return new BinaryContentResult(buffer, "application/pdf");
}

The RenderActionResultToString helper method:

protected string RenderActionResultToString(ActionResult result)
{
    // Create memory writer.
    var sb = new StringBuilder();
    var memWriter = new StringWriter(sb);

    // Create fake http context to render the view.
    var fakeResponse = new HttpResponse(memWriter);
    var fakeContext = new HttpContext(System.Web.HttpContext.Current.Request, fakeResponse);
    var fakeControllerContext = new ControllerContext(new HttpContextWrapper(fakeContext), this.ControllerContext.RouteData, this.ControllerContext.Controller);
    var oldContext = System.Web.HttpContext.Current;
    System.Web.HttpContext.Current = fakeContext;

    // Render the view.
    result.ExecuteResult(fakeControllerContext);

    // Restore data.
    System.Web.HttpContext.Current = oldContext;

    // Flush memory and return output.
    memWriter.Flush();
    return sb.ToString();
}
役に立ちましたか?

解決

I'm not exactly sure what you're asking, but, when you call View(model) the view that is chosen is based upon conventions.

Here is an example:

public class HerbController : Controller {
    public ActionResult Cilantro(SomeType model) {
        return View(model)
    }
}

That will look for a view file called Cilantro.cshtml in a folder called Herb (Views/Herb/Cilantro.cshtml). The framework will also look in the Shared directory as well in case it is a view that is meant to be shared across multiple results.

However, you may also want to look at the Global.asax file to see if there are any custom view paths being setup for the view engine. The example I gave above is based upon the default conventions of ASP.NET MVC. You can override them to meet your needs better if needed.

他のヒント

The convention for views is that they are in a folder named after the controller (without "Controller") and the .cshtml file inside that folder is named after the calling action. In your case, that should be:

~/Views/[Controller]/ProposalPdf.cshtml

The logic to determine which view template will be used is in the ViewResult that is returned from the call

var viewModel = View(model);

And how the view is selected is determined by the configured ViewEngine(s), but it will use the current Area, Controller and Action route values to determine what view should be served.

What the route values are for the ProposalPDF action will depend on how your routing is configured, but assuming the defaults, the action route value will be ProposalPDF, the controller route value will be the name of the controller class in which this action resides (minus the Controller suffix) and the area will be the area folder in which the controller lives, with a value of empty string if in the default controller folder. Then using these route values, a view will be looked up in the Views folder using the following convention

~/Views/{Area}/{Controller}/{View}.cshtml

There is always Glimpse that can help with providing runtime Diagnostics too, such as which View file was used to serve up the returned page, although I'm not sure how this would look when a ViewResult is executed internally to provide the contents of a file.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top