I'm working on setting up separate projects for my Areas in my main MVC project. I followed the guide here:
https://stackoverflow.com/a/12912161/155110
I set breakpoints during the launch and can see that the RazorGeneratorMvcStart is being hit as well as the AreaRegistration that sets up the routes. RazorGenerator.Mvc is installed and the Custom Tool on my cshtml pages is set to use it. When I access the Area URL after launching my main project, I can see that it hits the controller in the separate Area project, however, I cannot get it to find the view. I get the following which a huge list of locations:
[InvalidOperationException: The view 'Index' or its master was not
found or no view engine supports the searched locations. The following
locations were searched:
STARAreaRegistration.cs
using System.Web.Mvc;
namespace AreaSTAR.Areas.STAR
{
public class STARAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "STAR";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"STAR_default",
"STAR/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
}
}
RazorGeneratorMvcStart.cs
using System.Web;
using System.Web.Mvc;
using System.Web.WebPages;
using RazorGenerator.Mvc;
[assembly: WebActivatorEx.PostApplicationStartMethod(typeof(AreaSTAR.RazorGeneratorMvcStart), "Start")]
namespace AreaSTAR {
public static class RazorGeneratorMvcStart {
public static void Start() {
var engine = new PrecompiledMvcEngine(typeof(RazorGeneratorMvcStart).Assembly) {
UsePhysicalViewsIfNewer = HttpContext.Current.Request.IsLocal
};
ViewEngines.Engines.Insert(0, engine);
// StartPage lookups are done by WebPages.
VirtualPathFactoryManager.RegisterVirtualPathFactory(engine);
}
}
}
Areas/STAR/Controllers/DefaultController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace AreaSTAR.Areas.STAR.Controllers
{
public class DefaultController : Controller
{
// GET: STAR/Default
public ActionResult Index()
{
return View();
}
}
}
Areas/STAR/Views/Default/Index.cshtml:
#* Generator: MvcView *#
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>View1</title>
</head>
<body>
<div>
Index view
</div>
</body>
</html>
URL accessed when seeing the View not found error: http://localhost:53992/STAR/Default/Index
This was because I accidentally installed the RazorEngine.Generator extension and set the custom tool to RazorEngineGenerator instead of installing the Razer Generator extension and setting the custom tool to RazorGenerator.
Related
I have an application in Asp.NET MVC with Entity Data Model. Now I have a model for state_master table where I have only one property 'State_Name' and in database table I have 2 fields 'State_ID' which is auto incremented and 'State_Name' for which I will insert the data.
Now as I have only one property in Model I am not able to fetch both the fields 'State_ID' and 'State_Name' using this model. This is the issue which I am facing.
State Model File :
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
namespace ViewData.Models
{
public class state_model
{
[Key]
public string state_name { get; set; }
}
}
View File :
#using ViewData.Models
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>state_view</title>
</head>
<body>
<div>
<ul>
#foreach (var state in ViewData["states"] as IList<state_model>)
{
<li>
#state.state_name
</li>
}
</ul>
</div>
</body>
</html>
Controller File :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using ViewData.DataAccessLayer;
using ViewData.Models;
namespace ViewData.Controllers
{
public class StateController : Controller
{
// GET: State
public ActionResult Index()
{
DataLayer dl = new DataLayer();
IList<state_model> data = (from o in dl.states select o).ToList();
ViewData["states"] = data;
return View("state_view");
}
}
}
You need to create a View Model for state to fetch and bind all the fields from database. For example,
public class state_ViewModel
{
[Key]
public int state_Id { get; set; } //check with your data type
public string state_name { get; set; }
}
So in your controller method, You can use this one for fetch and bind. And for insert record you can use state_model
And in your action method,
public ActionResult Index()
{
DataLayer dl = new DataLayer();
IList<state_ViewModel> data = (from o in dl.states select o).ToList();
ViewData["states"] = data;
return View("state_view");
}
See, View Model is nothing but rendering your data in your view.
To know more about view model click here
using mefcontrib.mvc3 on my mvc web application. used following class for initialize mef before application start event. That class is auto generated(downloaded) from nuget. some tiny changes applied for catalog(for plugins folder) and controller factory segments.
[assembly: WebActivator.PreApplicationStartMethod(typeof(OMR.CMS.App_Start.MefContribMVC3), "Start")]
namespace OMR.CMS.App_Start
{
using System.ComponentModel.Composition.Hosting;
using System.Linq;
using System.Web.Mvc;
using MefContrib.Hosting.Conventions;
using MefContrib.Web.Mvc;
public static class MefContribMVC3
{
public static void Start()
{
// Register the CompositionContainerLifetimeHttpModule HttpModule.
// This makes sure everything is cleaned up correctly after each request.
CompositionContainerLifetimeHttpModule.Register();
// Create MEF catalog based on the contents of ~/bin.
//
// Note that any class in the referenced assemblies implementing in "IController"
// is automatically exported to MEF. There is no need for explicit [Export] attributes
// on ASP.NET MVC controllers. When implementing multiple constructors ensure that
// there is one constructor marked with the [ImportingConstructor] attribute.
var catalog = new AggregateCatalog(
//new DirectoryCatalog("bin"),
new DirectoryCatalog("Plugins"),
new ConventionCatalog(new MvcApplicationRegistry())); // Note: add your own (convention)catalogs here if needed.
// Tell MVC3 to use MEF as its dependency resolver.
var dependencyResolver = new CompositionDependencyResolver(catalog);
DependencyResolver.SetResolver(dependencyResolver);
// Tell MVC3 to resolve dependencies in controllers
ControllerBuilder.Current.SetControllerFactory(new MyMEFControllerFactory(dependencyResolver));
// Tell MVC3 to resolve dependencies in filters
FilterProviders.Providers.Remove(FilterProviders.Providers.Single(f => f is FilterAttributeFilterProvider));
FilterProviders.Providers.Add(new CompositionFilterAttributeFilterProvider(dependencyResolver));
// Tell MVC3 to resolve dependencies in model validators
ModelValidatorProviders.Providers.Remove(ModelValidatorProviders.Providers.OfType<DataAnnotationsModelValidatorProvider>().Single());
ModelValidatorProviders.Providers.Add(
new CompositionDataAnnotationsModelValidatorProvider(dependencyResolver));
// Tell MVC3 to resolve model binders through MEF. Note that a model binder should be decorated
// with [ModelBinderExport].
ModelBinderProviders.BinderProviders.Add(
new CompositionModelBinderProvider(dependencyResolver));
}
}
}
and using followin code on my application class:
using OMR.Core.Web.Filters;
using OMR.Core.Web.Routing;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
namespace OMR.CMS
{
public class MvcApplication : System.Web.HttpApplication
{
[ImportMany(typeof(IFilterRegistration))]
public IEnumerable<Lazy<IFilterRegistration>> Filters { get; set; }
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
BundleConfig.RegisterBundles(BundleTable.Bundles);
RegisterFilters();
RegisterRoutes();
}
private void RegisterFilters()
{
var filters = GlobalFilters.Filters;
var filterRegistrations = DependencyResolver.Current.GetServices<IFilterRegistration>();
foreach (var registar in filterRegistrations)
registar.Register(filters);
}
public void RegisterRoutes()
{
var routes = RouteTable.Routes;
var routeRegistrations = DependencyResolver.Current.GetServices<IRouteRegistration>();
// Ignores
foreach (var registrar in routeRegistrations)
registrar.Ignore(routes);
// Routes
foreach (var registrar in routeRegistrations)
registrar.Route(routes);
}
}
}
The problem is following property is currently null on application start method.
ImportMany(typeof(IFilterRegistration))]
public IEnumerable<Lazy<IFilterRegistration>> Filters { get; set; }
But following lines is working:
var filterRegistrations = DependencyResolver.Current.GetServices<IFilterRegistration>();
Why my import declaration isn't work?
I have online form builder appplication in ASP.NET MVC3 with Razor views.
It is similar to this - https://examples.wufoo.com/forms/workshop-registration/
I need users to be able to customize the page design.
Not only to upload a custom css, but also to customize the HTML page template.
Let's say users should have complete control on Layout's HTML for their custom webform page. User should be able to edit any HTML on the page, beside the form that is included into the layout.
I'm not sure how to do that with Razor and ASP.NET MVC 3.
Is it possible to:
load layout somewhere from database as string or whatever
replace some custom tags like "FORM1_INCLUDE" to
#Html.Partial("some_non_customizable_layout_for_form1")
use the result as a valid Layout file for the user's form page
Maybe 1-3 is not the best way to do what I need.
What can you suggest for such user defined page layout approach in ASP.NET MVC 3 with Razor views?
UPDATE 1
Using VirtualPathProvider I was able to load View from database, but it just returns text like:
#inherits System.Web.Mvc.WebViewPage
<body>
#Html.EditorFor(z => z.Customer)
</body>
and doesn't process any Razor syntax at all.
What could be the problem with it?
SOLVED:
Needed to place this line as the first one in Application_Start() method:
HostingEnvironment.RegisterVirtualPathProvider(new MyVirtualPathProvider());
UPDATE 2
Custom View Provider is registered in Global.asax.cs as:
protected void Application_Start()
{
HostingEnvironment.RegisterVirtualPathProvider(new MyVirtualPathProvider());
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
MyVirtualPathProvider code is:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Hosting;
using System.IO;
using System.Text;
namespace new_frontend
{
public class MyVirtualPathProvider : VirtualPathProvider
{
public override bool FileExists(string virtualPath)
{
var td = FindTemplate(virtualPath);
if (td == null)
{
return true;
//return base.FileExists(virtualPath);
}
else
{
return true;
}
}
public override VirtualFile GetFile(string virtualPath)
{
var td = FindTemplate(virtualPath);
if (td == null)
{
return new MyVirtualFile(virtualPath, "");
//return base.GetFile(virtualPath);
}
else
{
return new MyVirtualFile(virtualPath, td.ContentStep1);
}
}
private Facade.Dto.TemplateData FindTemplate(string virtualPath)
{
string prefix = "Template#";
int id = 0;
Facade.Dto.TemplateData td = null;
string fileName = System.IO.Path.GetFileNameWithoutExtension(virtualPath);
if (fileName.StartsWith(prefix))
Int32.TryParse(fileName.Substring(prefix.Length), out id);
if (id > 0)
td = Facade.FrontEndServices.GetTemplate(id);
return td;
}
}
public class MyVirtualFile : VirtualFile
{
private byte[] data;
public MyVirtualFile(string virtualPath, string body)
: base(virtualPath)
{ // 'System.Web.WebPages.ApplicationStartPage
string _body = /*body +*/ #"
#inherits System.Web.Mvc.WebViewPage
#using (Ajax.BeginForm(""Submit"", new AjaxOptions { UpdateTargetId = ""main"" }))
{
}
<!-- <PERSONAL_INFO> -->
<div id=""personal_info"" class=""op2-block"">
</div>
<!-- <PERSONAL_INFO> -->";
this.data = Encoding.UTF8.GetBytes(_body);
}
public override System.IO.Stream Open()
{
return new MemoryStream(data);
}
}
}
And now for the Razor view code defined as a string above I get this exception:
"Compiler Error Message: CS1061: 'System.Web.Mvc.AjaxHelper' does not contain a definition for 'BeginForm' and no extension method 'BeginForm' accepting a first argument of type 'System.Web.Mvc.AjaxHelper' could be found (are you missing a using directive or an assembly reference?)"
And when I changed Razor View code to:
string _body = /*body +*/ #"
#using System.Web.WebPages;
#using System.Web.Mvc;
#using System.Web.Mvc.Ajax;
#using System.Web.Mvc.Html;
#using System.Web.Routing;
#inherits System.Web.Mvc.WebViewPage<dynamic>
#using (Ajax.BeginForm(""Submit"", new AjaxOptions { UpdateTargetId = ""main"" }))
{
}
<!-- <PERSONAL_INFO> -->
<div id=""ppg_op2_personal_info"" class=""op2-block"">
</div>
<!-- <PERSONAL_INFO> -->";
I got a different error:
Type 'ASP._Page__appstart_cshtml' does not inherit from 'System.Web.WebPages.ApplicationStartPage'
When I change
#inherits System.Web.Mvc.WebViewPage
to
#inherits System.Web.WebPages.ApplicationStartPage
to fix the error above, I get a new one:
"Compiler Error Message: CS0103: The name 'Ajax' does not exist in the current context"
UPDATE3:
I tried to use base.XXX:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Hosting;
using System.IO;
using System.Text;
namespace new_frontend
{
public class MyVirtualPathProvider : VirtualPathProvider
{
public override bool FileExists(string virtualPath)
{
var td = FindTemplate(virtualPath);
if (td == null)
{
//return true;
return base.FileExists(virtualPath);
}
else
{
return true;
}
}
public override VirtualFile GetFile(string virtualPath)
{
var td = FindTemplate(virtualPath);
if (td == null)
{
//return new MyVirtualFile(virtualPath, "");
return base.GetFile(virtualPath);
}
else
{
return new MyVirtualFile(virtualPath, td.ContentStep1);
}
}
private Facade.Dto.TemplateData FindTemplate(string virtualPath)
{
string prefix = "Template#";
int id = 0;
Facade.Dto.TemplateData td = null;
string fileName = System.IO.Path.GetFileNameWithoutExtension(virtualPath);
if (fileName.StartsWith(prefix))
Int32.TryParse(fileName.Substring(prefix.Length), out id);
if (id > 0)
td = Facade.FrontEndServices.GetTemplate(id);
return td;
}
}
public class MyVirtualFile : VirtualFile
{
private byte[] data;
public MyVirtualFile(string virtualPath, string body)
: base(virtualPath)
{ // 'System.Web.WebPages.ApplicationStartPage
string _body = /*body +*/ #"
#inherits System.Web.Mvc.WebViewPage<PPG.Facade.Dto.NewOrderPageData>
#using (Ajax.BeginForm(""Submit"", new AjaxOptions { UpdateTargetId = ""main"" }))
{
}
<!-- <PERSONAL_INFO> -->
<div id=""personal_info"" class=""op2-block"">
</div>
<!-- <PERSONAL_INFO> -->";
this.data = Encoding.UTF8.GetBytes(_body);
}
public override System.IO.Stream Open()
{
return new MemoryStream(data);
}
}
}
In this case I get a view that is not parsed at all, this is what I get in the web browser:
#using System.Web.WebPages;
#using System.Web.Mvc;
#using System.Web.Mvc.Ajax;
#using System.Web.Mvc.Html;
#using System.Web.Routing;
#inherits System.Web.Mvc.WebViewPage<PPG.Facade.Dto.NewOrderPageData>
#using (Ajax.BeginForm("Submit", new AjaxOptions { UpdateTargetId = "main" }))
{
}
<!-- <PERSONAL_INFO> -->
<div id="ppg_op2_personal_info" class="op2-block">
</div>
<!-- <PERSONAL_INFO> -->
You should create a virtual path provider which fetches your custom views from a database.
There are several questions here about them. Just search for VirtualPathProvider
Updates (from my comments to the question discussion)
The VirtualPathProvider must be registered in Application_Start using HostingEnvironment.RegisterVirtualPathProvider(new MyVirtualPathProvider());
The base class MUST be called for all files that you can't currently serve. This is required since there can only be one VirtualPathProvider. (You'll see lots of strange errors otherwise)
#model directive doesn't work in files that you serve. You must use #inherits System.Web.Mvc.WebViewPage<YourNameSpace.YourModelName> instead.
IIRC you also need to override GetCacheDependency and return null for your own resources.
depending on the degree of flexibility and customization you wish to give this can be a very simple to a very time consuming task.
If you want users to simply customize the HTML on the page then you can use a WYSIWYG editor and store the raw html in the database.
In the view, use
#Html.Raw(Model.body) // Where body is the field containing the wysiwyg content
This will render the markup as-is.
To include custom tag / replacement you will have to define a list of string constants that can be inserted while using the WYSIWYG editor. These can then be search - replaced when displaying.
For example in your controller or model:
model.body.replace("$[form1]", "<form action='something' method='post' name='form1'></form>");
Of course depending on the nature of your application you might want to re-factor this into some sort of tag => markup conversion which will allow you to add more custom tags and their respective real HTML markups.
Hope this helps, Cheers!
I think you have to use something like this:
http://vibrantcode.com/blog/2010/11/16/hosting-razor-outside-of-aspnet-revised-for-mvc3-rc.html
I've done something similar & you may fine that XML is very useful. You save the layout definition as XML to the DB. That means it's easy to manipulate, either directly or by serialise/deserialise into an object model.
When you want to display the page, use XSLT to transform your XML into HTML, applying styles etc to the output.
I'm looking at the N2 CMS Minimal Example for MVC (from here)
I've figured out most of it, but I see that N2 supports 'Parts' that you can drop into 'Zones'.
How do I get Zones and Parts working in the minimal example?
The Html.Zone() command doesn't seem to work out-of-the-box.
With a bit of help from libardo at the N2 forum
Here's the 'minimal' way of adding Zones and Parts to the N2 Minimal Example for MVC:
1) Add this namespace in the web.config pages.namespaces node:
<pages>
<namespaces>
...
<add namespace="N2.Web.Mvc.Html"/>
...
2) Add a Container page model, using the AvailableZones attribute:
using N2.Integrity;
...
[Definition("ContainerPage")]
[AvailableZone("Right", "MyRightZone")]
public class ContainerPage : N2.ContentItem
{
...
3) Add Container controller in the usual N2 manner, nothing special needed here to make it a container:
[Controls(typeof(ContainerPage))]
public class ContainerController : ContentController<ContainerPage>
{
...
4) In the view for the container, use the Html.DroppableZone function:
<div class="n2zone">
<% Html.DroppableZone("MyRightZone").Render(); %>
</div>
5) Add a part model, e.g. this one just shows Title as a string. Note that PartDefinition is what makes it a Part that can be dropped into a Zone:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using N2;
using N2.Details;
namespace MyProject.Models
{
[PartDefinition("SimplePart")]
[WithEditableTitle("Title", 10)]
public class SimplePart : ContentItem
{
[DisplayableLiteral()]
public override string Title
{
get { return base.Title; }
set { base.Title = value; }
}
}
}
6) Add a Controller for the Part. This is the usual N2 controller except that we override Index to return a PartialView:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using N2.Web;
using N2.Web.Mvc;
using MyProject.Models;
namespace MyProject.Controllers
{
[Controls(typeof(SimplePart))]
public class SimplePartController : ContentController<SimplePart>
{
public override ActionResult Index()
{
return PartialView(CurrentItem);
}
}
}
7) Finally, add a partial view for the Part controller. Nothing special is needed here:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MyProject.Models.SimplePart>" %>
<div class="simplePart">
<%= Html.DisplayContent(m => m.Title) %>
</div>
In the N2 editor you can then drop as many SimpleParts as you like into the ContainerPage pages.
I have a base controller class and I would like to pass a Message from the Base class to all controllers and for that message to be available to all views.
I've created a basic version below...
Section Controller
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace Website.Controllers
{
public class SectionController : Controller
{
//
// GET: /Section/
public ActionResult Section()
{
ViewData["Message"] = "THIS IS A TEST";
return View();
}
}
}
Home Controller
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace Website.Controllers
{
public class HomeController : SectionController
{
public ActionResult Index()
{
return View();
}
}
}
View
<%= Html.Encode(ViewData["Message"]) %>
I know I can do this in the home controller but I'm just testing at the mo.
I'm not getting any errors with the above but I'm also not displaying the message on my view?
I'm using this tutorial http://www.asp.net/LEARN/mvc/tutorial-13-cs.aspx The Good Solution part, if that helps.
Think I've got it working now used the code below on my sectionController...
namespace Website.Controllers
{
public class SectionController : Controller
{
//
// GET: /Section/
public SectionController()
{
ViewData["Message"] = "THIS IS A TEST";
//return View();
}
}
}
Is this an ok solution?
You're setting your ViewData in the Section action method of your base controller, do you actually want to be setting it in the constructor of your base controller?
public SectionController()
{
ViewData["Message"] = "THIS IS A TEST";
}
HomeController.Index isn't calling SectionController.Section.
Because none of the requests are mapped to action "Section" in SectionController. If you mapped a request like domain/Section/Section, you would see your message in your view (Assuming that you are using default routing and have a view named "Section").
What you need to do is, placing your message into the viewdata on a method that runs every time an action is run. You can do it in OnActionExecuting like:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
ViewData["Message"] = "THIS IS A TEST";
base.OnActionExecuting(filterContext);
}
in the SectionController.