I've seen all the questions and answers around not having code-behind for a view, however I have a case where I need complex logic to generate the presentation (view) layer. I have to output a PDF file based on data obtained from db. Where is the best place to generate this PDF and write to the response stream? Doing response.write from the controller feels very wrong to me, but I would like responses to this, and to using a code-behind file for the view to generate the PDF. I suppose I could encapsulate the data in a viewmodel class and pass that to a Helper method to generate the output as well, what would be considered best practice in this case, specifically having a lot of logic around creating the PDF?
I would create a ActionResult class for this and return that from the controller. The ActionResult class is responsible for writing stuff to the output stream.
The better way to do it is by defining an ActionResult specific for outputting pdf files. This way you can reuse the code easily also in other applications
Related
Is it possible in ASP.NET MVC 5 to dynamically create a partial view (cshtml) in the /Views/Shared directory? I have a situation where people are going to upload a bunch of HTML as strings and was thinking it would be better for performance to store them on the file system.
Is it as simple as creating a new file, steaming a string and saving?
Yes it is possible
Simply make a view like DynamicView.cshtml
#model DynamicView
#Html.Raw(Model.HTMLString)
Now the method you will use to store both the HTML and the pointer to it is a different story. You can either store the sanitized HTML in a data-base and retrieve it with a call to the Controller like
public ActionResult DynamicView(ind id)
{
DynamicView model = new DynamicView();
DynamicView.HTMLString = dbContext.HTMLViews.Where(v => v.id == id);
return View(model);
}
If you wish to write the submitted HTML to files instead, you can instead do
public ActionResult DynamicView(string filePath)
{
DynamicView model = new DynamicView();
DynamicView.HTMLString = ...code that reads file
return View(model);
}
See this related post Writing/outputting HTML strings unescaped
Quick Answer: No (you cannot/should not modify Views Folder at RunTime.)
ASP.NET MVC Views and Shared Views are meant to be compiled and run. Modifying them or Adding new ones as the Application is running is not at all advisable or practical.
What would make more sense is for you to store the uploaded html blobs in a database, file system, storage blob (cloud). Then code your Shared View or Specific Views to retrieve specific html blobs from the stored location depending upon which user is logged in.
There are a whole lot of Extension Functions in MVC that Enable you to insert partial html in views. take a look at PartialExtensions as an starting point.
I had a requirement to create top menu which will generate from DB. I tried a lot to create it using partial view. But i found most of the cases partial view is use for static content. at last i fond some helpful tutorial please find the following i hope it will help you too.
click here to see how to create Dynamic Partial view
Should we move logic that supposes to be in Controller (like the data to render the partial view) to ActionFilter?
For example, I'm making a CMS web site. There should be a advertisement block to be rendered on several pages but not all the pages. Should I make an ActionFilter attribute like [ShowAd(categoryId)] and decorate the action methods with this attribute?
The implementation of this controller would include service calls to retrieve information from database, buildup view models and put in the ViewData. There would be a HtmlHelper to render the partial view using the data in ViewData if it exists.
That just seems yucky to me.
When I'm trying to figure out whether I need an ActionFilter, the first question I have is, Is this a cross-cutting concern?. Your particular use-case doesn't fit this, at first blush. The reason is, is that an ad is just another thing to render on a page. There's nothing special about it that makes it cross-cutting. If you replaced the word 'Ad' with 'Product' in your question, all the same facts would be true.
So there's that, and then there's the separation of concerns and testability. How testable are your controllers once you have this ActionFilter in place? It's something else you've got to mock out when testing, and what's worse is that you have to mock out those dependencies with every controller you add the ActionFilter to.
The second question I ask is, "How can I do this in a way that seems most idiomatic in the platform I'm using?"
For this particular problem, it sounds like a RenderAction and an AdController is the way to go.
Here's why:
An Ad is its own resource; it normally isn't closely tied to anything else on the page; it exists in its own little world, as it were.
It has its own data-access strategy
You don't really want to repeat the code to generate an Ad in every place you could use it (which is where a RenderPartial approach would take you)
So here's what such a beast would look like:
public AdController : Controller
{
//DI'd in
private AdRepository AdRepository;
[ChildActionOnly]
public ActionResult ShowAd(int categoryId)
{
Ad ad = Adrepository.GetAdByCategory(categoryId);
AdViewModel avm = new AdViewModel(ad);
return View(avm);
}
}
Then you could have a custom partial view that is set up around this, and there's no need to put a filter on every action (or every controller), and you don't have try to fit a square peg (an action filter) in a round hole (a dynamic view).
Adding an Ad to an existing page then becomes really easy:
<% Html.RenderAction("ShowAd", "Ad" new { categoryId = Model.CategoryId }); %>
If your ad system is simple enough, there is no reason you could/should not use an action filter to insert enough info into the view data to generate the ad in your view code.
For a simple ad system, say.. a single ad of a specific category shows up in the same place in the layout on every page and that's it, then there is no real argument of a better way except to prepare for future changes to the system. While those concerns may be legitimate, you may also have it on good authority that requirement will never change. But, even if requirements do change, having wrapped all the code that generates ads in one place is the most important aspect and will save you much more time up front than a more robust solution might. Obviously there are more than a few ways to wrap this code in a single place.
As for the way you are choosing to do it, I would keep your action filter cleaner to only have it insert the category into the view data and have all the magic happen inside your html helper which would take the category in as a parameter. Building up view models to shove into the view data is going to require a bit of extra work, and put code all over the place when it doesn't need to be there. Keep it simple and do all of the html generation inside of the html helper which is responsible for...building html.
What is the appropriate way of rendering a child template?
And what's the difference? Both seem to work for me.
And why does #Html.RenderPartial() no longer work?
Html.Partial("MyView")
Renders the "MyView" view to an MvcHtmlString. It follows the standard rules for view lookup (i.e. check current directory, then check the Shared directory).
Html.RenderPartial("MyView")
Does the same as Html.Partial(), except that it writes its output directly to the response stream. This is more efficient, because the view content is not buffered in memory. However, because the method does not return any output, #Html.RenderPartial("MyView") won't work. You have to wrap the call in a code block instead: #{Html.RenderPartial("MyView");}.
RenderPage("MyView.cshtml")
Renders the specified view (identified by path and file name rather than by view name) directly to the response stream, like Html.RenderPartial(). You can supply any model you like to the view by including it as a second parameter
RenderPage("MyView.cshtml", MyModel)
I prefer
#RenderPage("_LayoutHeader.cshtml")
Over
#{ Html.RenderPartial("_LayoutHeader"); }
Only because the syntax is easier and it is more readable. Other than that there doesn't seem to be any differences functionality wise.
EDIT: One advantage of RenderPartial is you don't have to specify the entire path or file extension it will search the common places automatically.
The RenderPartial method doesn’t return HTML markup like most other helper methods. Instead, it writes
content directly to the response stream, which is why we must call it like a complete line of C#, using a semicolon.
This is slightly more efficient than buffering the rendered HTML from the partial view, since it will be written to the
response stream anyway. If you prefer a more consistent syntax, you can use the Html.Partial method, which
does exactly the same as the RenderPartial method, but returns an HTML fragment and can be used as
#Html.Partial("Product", p).
We can also pass model using partial views. #Html.Partial("MyView","MyModel");
#RenderPages()
The above does not work in ASP.NET MVC. It only works in WebPages.
#Html.Partial("_Footer")
You will need to use the above in ASP.NET MVC.
For ASP.NET Core 7. In the Shared folder make partial file then user this following code
<partial name="_NavBar" />
I have a partial view that should not be cached in a output cached MVC view.
Usually you write non-cached content by using Response.WriteSubstitution.
The problem is that WriteSubstitution takes as a parameter a HttpResponseSubstitutionCallback callback which looks like this:
public delegate string HttpResponseSubstitutionCallback(System.Web.HttpContext context)
This is where things get complicated since there is no easy/fun way to generate the html on the fly.
You have to do a hack like this.
So the question is: Is there an easier way to make a partial view not cached ?
See Phil Haack's article on donut caching in MVC. Phil takes advantage of the existing API to create a new HtmlHelper method to provide a callback that can render the non-cached code. His supplies an anonymous method to the helper to specify the callback. To get this to work unchanged, you'll still need to have a method that renders a partial view to a string, though I think it would be easier to do in an HtmlHelper -- just look at the source for RenderPartial and RenderPartialInternal and see what changes would be needed to write it to a MemoryStream instead of the Response -- I believe it would be the same code except you'd supply your stream instead of the Response output stream, then convert your stream to a string.
It might look like this:
<%= Html.Substitute( cb => Html.RenderToString( "Partial" ) ) %>
Phil indicates that it might make it in to ASP.NET MVC 1.0, but I think it's only available in the MvcFutures assembly.
In MVC2 we can use Html.Action to easily obtain the substituted html. Yeeeeiii
But Response.WriteSubstitotion is not working anymore. Aaaahhhh
Just wondering what people think about creating an area to hold/manage json based requests (note I am thinking mostly get data not post data). I know its not your typical use of an area (i.e. normally you would create a different area for blog vs forum) but I am getting to the point where my project isn't huge but I definitely have a a lot of json stuff which seems to be confusing the issue and is making things look "unclean".
For instance, at the bottom of each controller is where I am putting json actions and so that they don't get mixed up with the other actions I prefix them with json - I shouldn't have to do this... Also I have specific view models for json which I have to prefix with json as well... etc, etc.
It would seem much cleaner to have them off in their own area and be able to drop the json prefix all together and have that defined by the area... what do you think or is this a bad idea?
Cheers
Anthony
I think it's a good idea. Having an asynchronous area where all controllers implement only asynchronous actions will certainly clean up your code. The problem will come when your project does become so big you want to expand into regular areas, then you'll end up with some naming conventions that might end up a bit messy.
You could also just create a separate controller for your json actions. I think this makes more sense than creating an area. Do you need json specific views, content, model etc or just some asynchronous actions?
I think that it's not nessesery to create separate area or even separate actions. If the actions return the same data and differ only in the type of the request - ajax or non-ajax you can just check what is the request and use the corresponding data format.
public ActionResult Index()
{
MyViewModel model = DataAccess.GetMyViewModel(); // Data access code
if (Request.IsAjaxRequest())
{
return Json(model);
}
else
{
return View(model);
}
}