Is it possible to put ASP.NET Areas in a class library? - asp.net-mvc

I would like to be able to reuse an area in one of my MVC project in multiple MVC projects. Can I put my area in a class library that can later be reused in a new MVC project?
Thanks,

Not out of the box. You may take a look at MVCContrib Portable Areas which use a custom VirtualPathProvider allowing you to embed areas as resources into separate assemblies. If you are using ASP.NET MVC 3 you could also take a look at the following blog post.
Be careful though as this might not work if you precompile your web application.

You can have the AreaRegistration class and the Controllers in a library. The ASP.NET MVC runtime will register them automatically.
But the the views need to be in any of the following locations:
~/Areas/[AreaName]/Views/[ControllerName]/[ViewName].cshtml
~/Areas/[AreaName]/Views/Shared/[ViewName].cshtml
~/Views/[ControllerName]/[ViewName].cshtml
~/Views/Shared/[ViewName].cshtml
Or you can specify them yourself by setting them when registering the View Engine:
var viewEngine = new RazorViewEngine {
AreaViewLocationFormats = new[] { "~/MySharedAreas/{2}/Views/{1}/{0}.cshtml" }
};
2 is area name, 1 is controller name and 0 is action name.
It depends on the purpose. If you just want to reuse the controllers this method will work, but if you want to reuse the Views as well, then you have to go with the solution mentioned by Dimitri

Related

Sitecore multisite MVC solution

I'm new to Sitecore MVC and currently with web forms I have all the sites organized under:
\Website\Sites\Site1\css|js|Layouts|Sublayouts|etc.
\Website\Sites\Site{n}\css|js|Layouts|Sublayouts|etc.
I'm able to add an MVC site to my solution and works fine alongside the web forms sites; however, adding a second MVC site that happen to have the same controller/view names generates a conflict.
For example, if I create a controller for Site1
Controllers/Site1/FooController (has index and hello)
Then the views are:
Views/Foo/Index
Views/Foo/Hello
But if Site2 also has a controller with the same name then it's a conflict:
Controllers/Site2/FooController (has index and hello)
Then the views are:
Views/Foo/Index
Views/Foo/Hello
But they're used by Site1.
The question is how to setup two (or more) MVC sites that happen to have the same controller/view names. Is there a recommended way to structure the sites in the solution or do I have to override pipelines/processors?
Thanks
Update:
Thanks everyone. Areas solved my problem but introduced two new problems:
The conflict in the controller names which solved by putting the namespace, class and dll names in the controller name in Sitecore - reference: http://blog.xcentium.com/2014/03/sitecore-mvc-and-duplicate-controller-names/
When the controller returns a view, I have to put the full path of the view; otherwise, I get an error where the view is not found.
For example: return View("~/Areas/Site1/Views/Home/Index.cshtml");
I'm looking into a fix provided from a developer from Sitecore's forum:
http://www.chrisvandesteeg.nl/2014/06/13/sitecore-mvc-in-a-multisite-environment-areas/
I'll try it out and report back.
you need to use namespaces in routes.MapRoute, look at the below posts which have already discussed:
Is it possible, in MVC3, to have the same controller name in different areas?
Multiple MVC projects in a single solution
and below is the post by John west post which relates your situation:
http://www.sitecore.net/Community/Technical-Blogs/John-West-Sitecore-Blog/Posts/2012/06/Using-Web-Forms-and-MVC-in-a-Single-Solution-with-the-Sitecore-ASPNET-CMS.aspx
We had the similar problem and answer was to separate out every site with MVC areas and they works perfectly. Though we ran into issue of controller name duplication but that can be resolved by adding the namespace during the area route registration.
But a clean way to implement this is to let Sitecore know about the MVC areas and initialize your controller/action with area and namespace. This process has been blogged by Kevin and he has a package as well. It expect you to define the area name in controller rendering.
http://webcmd.wordpress.com/2013/01/24/sitecore-mvc-area-controller-rendering-type/
To avoid the hard coded path of view(s) you can always extend controller rendering template to add view path and create an action filter to add the view path after action is executed. Add the below code in action filter and register the filter in sitecore action filter registration pipeline.
public void OnActionExecuted(ActionExecutedContext filterContext)
{
ViewResult result= filterContext.Result as ViewResult;
if(result == null) return;
Rendering redering = RenderingContext.CurrentOrNull.With(x=>x.Rendering).Return(x=>x,null);
string viewName= rendering.Return(r=> r.GetFieldValue(CustomMvcSettings.ViewPathField), string.Empty);
if(String.IsnullOrEmpty(viewName)) return;
result.ViewName = viewName;
}
The best thing you can do is split your websites up in different projects in the same solution.
Building two websites in the same project can become unstructured and messy.
After that you can route the controllers with the same name using the different namespaces.
Sitecore mvc duplicate controller
Just to keep this topic in sync with the SDN forum,
I recommend using a sitecore specific constraint, as described at
http://www.chrisvandesteeg.nl/2014/06/13/sitecore-mvc-in-a-multisite-environment-areas/
This solution allows you to set the attribute mvcArea on your configuration node

Keeping hierachy in ASp.NET MVC - Use Areas or just routing?

I am new to ASP.NET MVC4.
I must build a number of utilities (~40-50) that are going to be used by 4 groups of users in our internal windows domain.
I have made a controller named SupportController and i plan to include in there the 40 actions i want.
Some of them are special to each group and some are common.
I want to create a frontpage by accessing HomeController -> Index action. This frontpage will include a global menu.
I want to have urls like:
www.mydomain.com/bg/nullfix
www.mydomain.com/bg/invoicecorrection
www.mydomain.com/ro/updatecurrency
www.mydomain.com/ro/deletecosting
www.mydomain.com/common/fixaddressbook
e.t.c
From what i have read i do this url structure by altering the route configuration, For example (covers the above example #1 url):
routes.MapRoute(
name: "BgNullifiedInvoice",
url: "bg/nullfix",
defaults: new { controller = "Support", action = "BgNullifiedInvoicesFix"}
);
Since this will be my entry level mvc project, it will grow over time...
So is it proper to get into using the Areas functionality of ASP.NET MVC or get by using just routing?
Personally I would suggest using areas in such a project. They exist to separate your software's functionality into modular, organised and easily maintainable groups each with their own set of routes - which is exactly what it sounds like you need.
One thing that's certain about software is change, and areas help ease that change.

MVCContrib portable areas not working from HtmlExtensions, MVC 3

I just implemented MVCContrib's Portable Area feature and it works fine. I can open it using:
http://localhost/projectname/portableAreaName, but this portable area is not working if i render it using the HtmlHelper extension method like this:
public static void RenderHtmlWidget(this HtmlHelper Html)
{
Html.RenderAction("Index", "HtmlWidget", new {area = "HtmlWidget"});
}
And calling the helper method in the view as such:
#using Project.Widgets.HtmlWidget;
#{Html.RenderHtmlWidget();}
I'm getting an error: The view 'Index' or its master was not found or no view engine supports the searched locations. In the possible location list there are no ~/areas/... defined.
But I can render my HtmlWidget successfully with this the same line of code in the view:
#{Html.RenderAction("Index", "HtmlWidget", new { area = "HtmlWidget" });}
What am I doing wrong and how should I use the HtmlHelper extensions correctly with the MVCContrib portable areas feature?
There are a few things that may be causing this.
In the calling/parent project where you use the helper method to invoke your portable area, do you have a Web.config file in the /Areas/ folder? If not, you must copy the Web.config found in the /Views/ folder of the same project, and simply place the new copy in the /Areas/ folder as well.
In the Registration class file in your portable area project, after you call MapRoute in the "RegisterArea" method, are you calling "RegisterAreaEmbeddedResources();"?
Is each view in your portable area project made to be an embedded resource as opposed to content? Select a View in the Solution Explorer and hit F4, "Build Action" should be set to "Embedded Resource", but it defaults to "Content"
You also need to make sure that both the Portable project and the consuming project reference the same version of MvcContrib, but that they also utilize the same version of ASP.NET MVC. If your area is referenced in multiple projects, each based off of a different version of MVC (not likely, but possible depending on the situation), your area must use whatever version of MVC the consuming project uses.
I'd also suggest using Phil Haack's .NET Routing Debugger - its a single DLL file that you reference in the consuming application and add a single line to your ApplicationStart() in your Global.asax.cs. This becomes incredibly helpful in determining if your portable area is being correctly registered with the base project - and helps you cut to the chase.

Changing ASP.Net MVC directory structure

I've been working with ASP.Net MVC (3) for some time now and i like it a lot. But one thing i find a bit annoying is having to browse between the controllers / views / model / script directory all the time. So i'm wondering if there's a way to tell MVC to look for the files in a different location?
Maybe someone can tell me how to simply group the files together by controller like:
Directory: /Membership
MembershipController
LogOnView
LogOnModel
RegisterView
RegisterModel
Kind regards
Olav
I know exactly what you're talking about. Here are the conditions where I find the default MVC folder structure to be onerous:
I'm using a model-per-view approach
My controller basically only works with that one particular view
I have some javascript that only pertains to that view
Why do I want to put each of these pieces in a different folder?
I create a folder for the view in the Views folder, so you have a folder ~/Views/MyEntityList (just like the traditional MVC approach), but I put everything that pertains to that component there:
~/Views/MyEntityList/
MyEntityListController.cs
MyEntityListModel.cs
MyEntityList.js
MyEntityList.aspx
I find this structure leads all the developers to keep views decoupled from one another. No special MVC configuration is required, except for allowing browsers to access the .js resources directly.
There are some architectural patterns where this might not be a good way to go. For a model-per-view approach (see Los Techies for more description) I really like this structure.
I think you need to get the Solution Navigator extensions via Power Tools update for VS 2010.
That way, you can display in the Solution Navigator, as opposed to the solution explorer, only the open files, for example. Makes it easier.
By the way, delete all the model folders and create a separate model project, eg:
MyApp.Domain
Any solution that is beyond basic will benefit from this.
As stated in the comments to your question, Areas will also reduce your navigation requirements.
The only "looking of files" going on is with views, everything else is just a convention, so if you want you could have:
Directory: /Membership
MembershipController
LogOnView
LogOnModel
RegisterView
RegisterModel
... but the views must be in ~/Views/Membership
It looks like you have to override some behavior in the view engine. You can See this question to get a better idea.
One way I can think of to achieve this is by writing your custom view engine. You can place all these below files in Controllers/Membership
MembershipController
LogOnView
LogOnModel
RegisterView
RegisterModel
Models will not be a problem you can simply change the namespace for the models, the only problem is with the views. For this write your custom view engine so that your mvc application knows the physical location of the view files as follows.
public class CustomViewEngine : RazorViewEngine
{
public CustomViewEngine()
{
ViewLocationFormats = new[]
{
"~/Controllers/{1}/{0}.cshtml",
};
}
}
In global.asax.cs add the ViewEngine in Application_Start() by including the following code
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new CustomViewEngine());
You may also have to take care of various other factors like updating the Layout attribute depending on where you place the _Layout.cshtml.
In case you are using areas, add the AreaViewLocationFormats string array as well.
You can do further customization by overriding some of the methods like FileExists, CreateView, CreatePartialView.
Note: Do not forget to copy web.config in the views folder to the Membership controller. Otherwise application does not find the required mvc namespaces and it does not find the symbols like viewbag, model etc.

Is there an equivalent to Monorail view components for the ASP.Net MVC Framework?

I make heavy use of View Components in some of the larger applications I've built in Monorail - What is the equivalent approach in ASP.Net MVC for a view component, that can support sections etc.?
Actually you have several options to create the equivalent of a ViewComponent in ASP.NET MVC, depending in the complexity of your component. I use these two approaches which are the more mvc-ish of the options I am aware of.
1:
The simplest thing is to create a ViewUserControl and display it using Html.RenderPartial with the helper. The ViewUserControl is a simple piece of markup with no backing controller (I think you can put a codebehind file if you want).
Optionally, you can pass a model object or the entire ViewData dictionary to the view when calling RenderPartial, like this:
<% Html.RenderPartial("TopBar", model); %>
"TopBar" is an ascx page. This works anywhere, in master pages and in normal views.
2:
If you want your component to have more complicated logic or to access datasources, IoC, etc, then you can use Html.RenderAction which is an extension method found in the Microsoft.Web.Mvc assembly. I am using this out of the mvccontrib distribution. It works like this, you need to create a normal controller with all the logic you need, then create some views and all of these things become your component, for example:
public class AboutComponentController : Controller {
public IRepository Repository{ get; set; }
public ActionResult Detail() {
var lastEvent = Repository.FindAll<Auditoria>().FirstOrDefault();
return View(lastEvent);
}
}
Notice how I have a reference to an IRepository which is going to be injected with IoC (Windsor in my case) and I can do anything a normal controller would do.
Now, in any page (master or normal) where you want to use your component, import Microsoft.Web.Mvc and call Html.RenderAction with the appropriate parameters. This will create a mini mvc pipeline that creates the controller, resolves the view, etc., just like a Monorail ViewComponent. I prefer to use the lambda based variation of the method, like this:
<% Html.RenderAction<AboutComponentController>(x => x.Detail("a message"));%>
Unfortunately, the only way to pass parameters is to use the method call itself, which in turn must be unique in the controller. Still needs some work to resemble a ViewComponent.
I don't use masterpages or layouts in the views of my components since they are composition elements themselves.
Remember that when using the Webforms view engine, you can have strongly typed views if you like to have intellisense when using the Model variable in code blocks.
The beauty of this is that you can mix view engines with these approaches, I usually create the components in nvelocity and display them in aspx pages, etc.
I now there can be issues with caching of the partial views but I haven't run into any so far. I am sure there are other options (like subcontrollers in mvccontrib) but this is usually enough for simple cases. Of course you can use normal ASP.net components in your aspx view pages but that would be cheating right? hehe. I hope it helps.
Phil Haack blogged about creating areas to group controllers into sub-folders/sections similar to MonoRails.

Resources