dynamically create partial view (CSHTML) - asp.net-mvc

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

Related

Multi Tenant Razor pages

I am trying to set up Razor Pages routing to allow different views to be rendered for different tenants.
I have a directory structure as follows:
/Pages
Test.cshtml.cs
/Tenant1
Test.cshtml
/Tenant2
Test.cshtml
Given I am already able to decide which tenant is required, how is it possible to configure the routing to map some path eg localhost:8080/Test to either Tenant1/Test or Tenant2/Test views.
Use dynamic view content (via partial views).
With this solution, the Test page will dynamically load a different view depending on the route used to call it.
This means that you only have a single Test page but inside the cshtml file you will grab content from a partial view (more on that in a second).
First you will need to rename the files like so....
/Pages
Test.cshtml.cs
/Tenant1
_Test.cshtml // note it is prefixed with an underscore!
/Tenant2
_Test.cshtml // prefixed with an underscore too.
The naming convention for a partial view is to prefix the file with an underscore (_). This will immediately identify to someone looking at your project files as a "non-routable" page.
Then you add a little bit of logic to render the partial views...
Test.cshtml
#{
switch(...) // used a switch statement to illustrate the solution
{
case "Tenant1":
await Html.PartialAsync("~/Pages/Tenant1/_Test.cshtml");
break;
case "Tenant2":
await Html.PartialAsync("~/Pages/Tenant2/_Test.cshtml");
break;
default:
throw new NotImplementedException();
}
}
You can read about partial views here.
Extra: Using the same page model.
I also noticed that you had wanted to use the same page model (meaning sharing Test.cshtml.cs for both. This is rather trivial, but for the sake of completeness of the answer here is how you would do that...
/Pages/Test.cshtml.cs
namespace Foo.Pages
{
public class MySharedTestModel : PageModel
{
...
}
}
/Pages/Tenant1/Test.cshtml and /Pages/Tenant2/Test.cshtml
#page
#using Foo.Pages
#model MySharedTestModel
...

Find MVC template view by name

I am writing an HtmlHelper extension and I need to search for the existence of a template by name. The template in question may be a display or editor template depending on the context. My initial thought was to use ViewEngines.Engines.FindPartialView method. However, it appears that this method is not searching the ~/Views/Shared/DisplayTemplates and ~/Views/Shared/EditorTemplates directories.
I suppose this is for good reason. After all, how would the ViewEngine know whether to return the display or editor template without some additional information of context?
So, that leads to the question: how can I search for a specific EditorTemplate/DisplayTemplate I've considered adding a custom view engine to the ViewEngines collection to include these locations. I'm concerned, however, that this might be problematic.
My main concern is that the DisplayTemplate/EditorTemplate view might be served up for something unintended. Does anyone else see this as a problem?
Is it a better idea just to new up a specific DisplayTemplateViewEngine/EditorTemplateViewEngine instance when necessary and keep the ViewEngines collection clear of this specific functionality?
Is there something else I'm missing?
I absolutely love that the MVC framework is open source! I was able to determine, from the TemplateHelpers class (internal to the MVC Runtime) that the DataBoundControlMode is considered when rendering a template. The answer was simple! All I have to do is prefix the template name with the appropriate template director. So, to find a display template:
var metadata = ModelMetadata.FromLambdaExpression(expression, HtmlHelper.ViewData);
ViewEngines.Engines.FindPartialView(
_controllerContext,
string.Format("DisplayTemplates/{0}", metadata.TemplateHint))
No additional view engines or routing required! In case you're interested in the application, my helper is auto-generating UI components for a given model. I wanted to enable the existence of a custom template to bypass the automated rendering.
A WebFormViewEngine has a few properties that define (patterns for) locations to search for views.
You either follow the convention of the view engine you use, or create a custom view engine (that for examlpe extends Razor) with custom view paths.
The latter is explained here:
public class CustomViewEngine : RazorViewEngine
{
public CustomViewEngine()
{
var viewLocations = new[] {
"~/Views/{1}/{0}.cshtml",
"~/Views/Shared/{0}.cshtml",
"~/Views/Shared/DisplayTemplates/{0}.cshtml",
"~/Views/Shared/DisplayTemplates/{1}/{0}.cshtml",
// etc
};
this.PartialViewLocationFormats = viewLocations;
this.ViewLocationFormats = viewLocations;
}
}
So I guess in your helper you should look up the current view engine and look up its view location paths and search them in order. Doesn't an Html helper have a method or property for getting the view you're currently running in?
Why you just map the relative path
string path = Server.MapPath("~/View/");
And then check if the file exits base on the .cshtml exit's in that specific directory
string fileName = "MyView.cshtml";
if (File.Exists(path + fileName))
//do somethings
else
//do another things

Retrieving and caching HTML from website using ASP.NET MVC 3

I want a partial view that display some stuff from a website that is not under my control.
The data on the website is only available through HTML, and thus I can only retrieve it by querying the web site and parsing the HTML. (The website holds a list of 50 elements, and I only want the top 10.)
Now, the data from the website is not changing very frequently, so I imagine that I can retrieve the HTML on an hourly basis, and displaying a cached version on my web site.
How can I accomplish this in ASP.NET MVC 3?
Ignoring the MVC3 requirement for now, you should look to using WebClient to grab the html from the website. You can do something like:
var client = new WebClient();
var html = Encoding.UTF8.GetString(client.DownloadData("http://www.somedomain.com"));
If you need to tailor your request, I'd recommend looking at HttpWebRequest, HttpWebResponse. Now that you can grab the html, you need to consider your caching mechanism, possibly in the ASP.NET runtime?
public ActionResult GetHtml()
{
if (HttpRuntime.Cache["html"] == null)
GetHtmlInternal();
return Content((string)HttpRuntime.Cache["html"], "text/html");
}
private void GetHtmlInternal()
{
var html = // get html here.
HttpRuntime.Cache.Insert("html", html, null, DateTime.Now.AddMinutes(60), Cache.NoSlidingExpiration);
}
The first solution that comes to mind is to create an action in a controller that makes an Http request to the remote web page and parses the html you want to return to your own page and then set output caching on your action.
Edit:
What controller to put the action in would depend on the structure of your web site and whether the partial view would be visible on all views or just a specific view. If the partial is visible in all views I'd either place it in the Home controller or create a "General" controller (if I anticipated more actions would go in such a controller).
If you want to manipulate the result I would probably make a model and partial view for the list. If you want to take a part of the returned html and output it as it is I would use the same method as in the answer by Matthew Abbott:
return Content(yourHtmlString);
The end would look something like this:
[OutputCache(Duration = 3600)]
public ActionResult RemoteList()
{
var client = new WebClient();
var html = Encoding.UTF8.GetString(client.DownloadData("http://www.somedomain.com"));
// Do your manipulation here...
return Content(html);
}
(Some of the above code was borrowed from the post by Matthew Abbott.)
You could just add OutputCache attribute on your action and set OutputCache.Duration Property to 3600 seconds (1 hour)

ASP.NET MVC Render View to a string for emailing

I want to use MVC views to create the body for an email and I have come across this (http://www.brightmix.com/blog/renderpartial-to-string-in-asp-net-mvc/) but it doesn't seem to work with strongly typed views (ViewContext is null). But I was after something the would render a full view including a masterpage.
I thought that if there was a way of just invoking a view with out redirecting and just writing to a different stream and sending the email within the controller that might do it, but I can't work out how to invoke a view.
Any suggestions would be great!
Thanks in advance.
Gifster
The question has been asked (and answered) already:
Render View as a String
This is the bit that I use:
protected string RenderViewToString<T>(string viewPath, T model, System.Web.Mvc.ControllerContext controllerContext) {
using (var writer = new StringWriter()) {
var view = new WebFormView(viewPath);
var vdd = new ViewDataDictionary<T>(model);
var viewCxt = new ViewContext(controllerContext, view, vdd, new TempDataDictionary(), writer);
viewCxt.View.Render(viewCxt, writer);
return writer.ToString();
}
}
Best place for this method is in a class library project that your mvc project has a reference to. Mainly because that way you can easily reuse it in all your apps. But also because it is neither Application logic (so doesn't belong in the controller) nor does it belong in the model. Some things are just utilities.
Note that to get this to work, the viewPath parameter HAS to be the PHYSICAL PATH to the file, complete with the .aspx extension. You can't use a route as WebFormView class requires a physical path in its constructor.
This will render the full view and take account of the master page.
HEALTH WARNING FOR HTML EMAILS:
HTML emails and the devices where you read them are even more difficult and restrictive to design for than websites and browsers. What works in one, will not work in another. So with html emails, you really have to Keep It Simple! Your lovely page with menus and relative images and whatever else, JUST WON'T WORK in all email devices. Just as an example, the images src attribute needs to be absolute and include the domain:
This won't work:
<img src="/Images/MyImage.gif" ... />
Bit this will:
<img src="http://www.mywebsite.com/Images/MyImage.gif" ... />
With those caveats, it works fine and I use it. Just don't try to send them the full gimmickry of your website, cos that won't work!
Even more important:
All CSS must be INLINE and just for basic styling: colours, borders, padding. But no floating and positioning. CSS layouts won't work consistently across devices!
You can use MvcView NuGet package to do this! Its simple, just install using install-package MvcMailer and you are done! Use full power of view engine to template, master page, view model and send html emails neatly!

Serving HTML or ASPX files with ASP.NET MVC

I want to implemented URL like :
www.domain.com/Business/Manufacturing/Category/Product1
This will give the details of specific product(such as specifications).
Since the list of Categories & Products in each Category are limited, I thought it is not worth to use database for products.
So I want to implement it using the HTML or ASPX files. Also I want the URL to be without the files extention(i.e. URL's should not have .html or .aspx extension)
How can I implement it with ASP.NET MVC? Should I use nested folder structure with HTML/ASPX files in it, so that it corresponds to URL? Then how to avoid extensions in URL?
I am confused with these thoughts
Asp.net Mvc uses the routing library from Microsoft. So it is very easy to get this kind of structure without thinking about the folder structure or the file extensions. With asp.new mvc you do not point a request at a specific file. Instead you point at a action that handles the request and use the parameters to determine what to render and send to the client. To implement your example you can do something like this:
_routes.MapRoute(
"Product",
"Business/Manufacturing/Category/Product{id}",
new {controller = "Product", action = "Details", id = ""}
);
This route will match the url you described and execute the action named "Details" on a controller named "ProductController" (if you are using the default settings). That action can look something like this:
public ActionResult Details(int id) {
return View(string.Format("Product{0}", id);
}
This action will then render views depending on what id the product have (the number after "Product" in the end of your example url). This view should be located in the Views/Product folder if you use the default settings. Then if you add a view named "Product1.aspx" in that folder, that is the view that will be rendered when you visit the url in your example.
All tough it is very possible to do it that way I would strongly recommend against it. You will have to do a lot of duplicated work even if you only have a few products and use partial views in a good way to minimize the ui duplications. I would recommend you use a database or some other kind of storage for you products and use a single view as template to render the product. You can use the same route for that. You just edit your action a little bit. It can look something like this:
public ActionResult Details(int id) {
var product = //get product by id from database or something else
return View(product);
}
This way you can strongly type your view to a product object and you will not have that much duplication.
The routing engine is very flexible, and when you have played around with it and learned how it works you will be able to change your url in just about any way you want without changing any other code or moving any files.
If you're not ready to dive into ASP.Net MVC, you can still get the nice URLs by using URL Rewriting for ASP.Net. That'd be simpler if you're already familiar with ASP.Net WebForms. The MSDN article on URL Rewriting should be a good start:
http://msdn.microsoft.com/en-us/library/ms972974.aspx
I'd be really sure you won't eventually have more products before deciding not to use a database. Both MVC and WebForms would allow you to make one page that dyamically shows a product and still have a nice URL- plus you might save yourself development time down the road. Something to think about.

Resources