Is it possible to access MVC Views located in another project?
Asked Answered
H

3

34

I want to separate my MVC project into several projects

So first of all, I've created two projects Front and Views

The Front project is a web application that contains controllers and models

The Views project is a class library project that will contains only the views

My question is how can I make controllers call views located in the Views project

I have controllers like this one:

public ActionResult Default()
        {
            return this.View();
        }
Herve answered 21/6, 2014 at 11:54 Comment(7)
how come you break MVC into MC & V as projects and using mvc . i wont advice you do that .Knobby
I used to work in some company, they have an mvc project breaked into thousands of projects, controllers project, models project, views project, ressources project(containing styles and skins ..) and services projects. Even views project, if I have a good memory, was breaked into a lot of projects depending on user Agent and device used.Herve
@supercool someone can do like this if following some architecture or desgin patternAccount
ya ehsan that is also possible . But making things complex rightKnobby
@supercool : no that is not making things complex, It is separating complex things to similar parts. I too was doing what you do, but now in a real company that works with other companies it is needed to separate the M,V and C into different projects as each part can be used in number of other projects.Carnay
Looks like VS2017 and MVC6 supports this https://mcmap.net/q/450819/-asp-net-mvc-6-view-components-in-a-separate-assemblyEnid
It's almost 7 years since this question was created. Did they somehow make it easier to implement everyone?Desiraedesire
L
31

MVC does not compile views into DLL's, but instead references them as files from the root of your site directory. The location, by convention is ~/Views and a search path is followed. This is more or less hard coded into the default view engines.

Because Views are files, when you break them into a separate project, they won't exist in your primary web application project. Thus, the view engine can't find them. When you compile the app, any projects referenced will only copy the DLL's (and potentially a few other things, like pdb's, etc.)

Now, there are ways to work around this, but to be honest, they're usually more trouble than they're worth. You can look into "Portable Areas" in the mvc contrib project, but these are not well supported and there's been talk of replacing them with NuGet packaging.

You can also follow @mo.esmp's advice, and create a custom view engine, but you'll still need to figure out ways to copy the Views somewhere the site can access them upon build and/or deploy.

My suggestion would be to NOT break out projects in the manner you describe. I don't see any value in it. If your project becomes so large, I would instead separate your code into areas, and keep all your area code and data together.

What value is there in separating items that are clearly dependent upon each other into separate assemblies who's only purpose is to collect things based on their purpose? I see some value in separating models into their own project, since models can be used by more than one assembly. Controllers and views, however, are only ever used by the MVC primary site.

Lexi answered 21/6, 2014 at 19:39 Comment(7)
Thats one good insight i completely agree with you erik . I would instead separate your code into areas well said , i prefer the same i.e create a separate layer for business logic and call the service methods to controller . cheersKnobby
One reason to separate controllers, views, client scripts might be to reuse them in other projects. For instance I have two otherwise unrelated projects, both require administrative user management.Randell
An excellent reason for this is the use of a single-page application that would be served by MVC and used in other places as well (such as mobile devices).Carmancarmarthen
one reason would be to have controllers and views which are specific to one customer, so you can keep the website all genericTulatulip
We have contractors working on some pages for us in their own project. Would be great if we could just reference their project and everything would just work... and it actually does except for this view issue. Sooo... there's on reason for having views in another project.Allgood
There are valid use cases for doing this. In my app I render PDFs via html strings. So any UI changes made auto reflect in PDF, ideal for maintainability. Now there are cases where the PDF my user wants is quite large, with noticeable ram and cpu usage.The most cost effective solution to relieve this memory and CPU pressure is to push the work into another compute instance that is ALREADY dedicated to various background tasks.Screenplay
@user2326106 - I don't think you're talking about the same thing we are. In fact, i'm not sure what you're talking about.Lexi
L
65

For including controllers you need to change your route registrations to tell them where to look for the controllers:

routes.MapRoute(name: "Default", url: "{controller}/{action}/{id}",
                namespaces: new[] {"[Namespace of the Project that contains your controllers]"},
                defaults: new {controller = "Home", action = "Index", id = UrlParameter.Optional});

For including views, create custom ViewEngine:

public class CustomViewEngine: RazorViewEngine
{
    public CustomViewEngine()
    {
        MasterLocationFormats = new string[]
        {
            "~/bin/Views/{1}/{0}.cshtml",
            "~/bin/Views/{1}/{0}.vbhtml",
            "~/bin/Views/Shared/{0}.cshtml",
            "~/bin/Views/Shared/{0}.vbhtml"

        };
        ViewLocationFormats = new string[]
        {
             "~/bin/Areas/{2}/Views/{1}/{0}.cshtml",
             "~/bin/Areas/{2}/Views/{1}/{0}.vbhtml",
             "~/bin/Areas/{2}/Views/Shared/{0}.cshtml",
             "~/bin/Areas/{2}/Views/Shared/{0}.vbhtml"
        };
        .
        .
        .
    }
}
protected void Application_Start()
{
    ViewEngines.Engines.Add(new CustomViewEngine());

For more information look at the default implementation of RazorViewEngin.

Here some good articles:

A Custom View Engine with Dynamic View Location

Using controllers from an external assembly in ASP.NET Web API

How to call controllers in external assemblies in an ASP.NET MVC application

How do I implement a custom RazorViewEngine to find views in non-standard locations?

Views in separate assemblies in ASP.NET MVC

Loy answered 21/6, 2014 at 18:41 Comment(6)
Instead of creating this yourself, the MvcCodeRouting has functionality for embedded views built in.Lading
Do we need the external assembly as a MVC project? if it's a class library what reference dlls do I need to include?Catlin
@Mohsen Esmailpour How should we refer to a specific project name in ViewLocationFormats ? for example is it like "Library2\Views\Accounts\List.cshtml" ?Turtleback
Links Are brokenBoot
New links added @YahyaHusseinLoy
@MohsenEsmailpour can you include an example of a path for views in a different project? #65298553Boot
L
31

MVC does not compile views into DLL's, but instead references them as files from the root of your site directory. The location, by convention is ~/Views and a search path is followed. This is more or less hard coded into the default view engines.

Because Views are files, when you break them into a separate project, they won't exist in your primary web application project. Thus, the view engine can't find them. When you compile the app, any projects referenced will only copy the DLL's (and potentially a few other things, like pdb's, etc.)

Now, there are ways to work around this, but to be honest, they're usually more trouble than they're worth. You can look into "Portable Areas" in the mvc contrib project, but these are not well supported and there's been talk of replacing them with NuGet packaging.

You can also follow @mo.esmp's advice, and create a custom view engine, but you'll still need to figure out ways to copy the Views somewhere the site can access them upon build and/or deploy.

My suggestion would be to NOT break out projects in the manner you describe. I don't see any value in it. If your project becomes so large, I would instead separate your code into areas, and keep all your area code and data together.

What value is there in separating items that are clearly dependent upon each other into separate assemblies who's only purpose is to collect things based on their purpose? I see some value in separating models into their own project, since models can be used by more than one assembly. Controllers and views, however, are only ever used by the MVC primary site.

Lexi answered 21/6, 2014 at 19:39 Comment(7)
Thats one good insight i completely agree with you erik . I would instead separate your code into areas well said , i prefer the same i.e create a separate layer for business logic and call the service methods to controller . cheersKnobby
One reason to separate controllers, views, client scripts might be to reuse them in other projects. For instance I have two otherwise unrelated projects, both require administrative user management.Randell
An excellent reason for this is the use of a single-page application that would be served by MVC and used in other places as well (such as mobile devices).Carmancarmarthen
one reason would be to have controllers and views which are specific to one customer, so you can keep the website all genericTulatulip
We have contractors working on some pages for us in their own project. Would be great if we could just reference their project and everything would just work... and it actually does except for this view issue. Sooo... there's on reason for having views in another project.Allgood
There are valid use cases for doing this. In my app I render PDFs via html strings. So any UI changes made auto reflect in PDF, ideal for maintainability. Now there are cases where the PDF my user wants is quite large, with noticeable ram and cpu usage.The most cost effective solution to relieve this memory and CPU pressure is to push the work into another compute instance that is ALREADY dedicated to various background tasks.Screenplay
@user2326106 - I don't think you're talking about the same thing we are. In fact, i'm not sure what you're talking about.Lexi
A
6

You can precompile your views - that way they are included in the dll and you can reference them from another project.

How to do it:

  1. Move the views to another project
  2. Install Razor Generator extension in Visual Studio
  3. Change Custom Tool to RazorGenerator for those views
  4. Add RazorGenerator.Mvc NuGet package to the view project
  5. Reference view project from your main project

That's it!

Although you'll need to do something with your models, either put them together with views or have a third project for them - otherwise you'll have a circular dependency.

Another drawback is that everyone who will be working with the views will need that Razor Generator extension.

The way this works is basically you make Visual Studio generate .cs files from your views in design time and those are a part of the compiled dll, same as any other piece of code.

Allrud answered 17/1, 2017 at 14:23 Comment(5)
Oh... nice. I think I'm going to try this out. Can you explain step #3 a bit more? Where is that happening?Melva
found it! It's in the properties of the cshtml file.Melva
Sounded promising, but I could only find RazorEngine.Generator with Nuget GUI which is not the same, when I try to install from marketplace.visualstudio.com/… it says it can't find compatible product to install to even though the page says it supports VS 2013 and that's what I'm using :(Allgood
You mentioned you tried to look for extension in the NuGets - it's slightly different thing. Could you try to look for menu item somewhere in "Tools" > "Extensions and Updates" in your Visual Studio? I have different version so I'm not totally sure it's in the same place in VS2013 - but you should look for Extensions and Updates instead of NuGets.Allrud
Thanks, this is points work fine with me and issue totally fixed.Caridadcarie

© 2022 - 2024 — McMap. All rights reserved.