Is it possible to access MVC Views located in another project? - asp.net

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();
}

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

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.

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:
Move the views to another project
Install Razor Generator extension in Visual Studio
Change Custom Tool to RazorGenerator for those
views
Add RazorGenerator.Mvc NuGet package to the view project
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.

Related

ASP.NET Core: how to override view from dll

Given a library referenced in a project. That library has precompiled views (see this post how to achieve it). So the lib has ABC.dll and ABC.PrecompiledViews.dll assemblies.
There's a view in library inside /Views/Shared/Index.cshtml. And a controler which returns it.
Then I have an application references the library (both assemblies). MVC discovers and returns that Index view in runtime.
Now I create a view inside application in Views/Shared/Index.cshtml. So its relative name is the same as in referenced view. By I doing this I mean that I want to override the view from the library.
When application is started from VS (Ctrl-F5) it works but the view in application is ignored.
When application is published (dotnet publish) then the application fails on start with the following error:
InvalidOperationException: The following precompiled view paths differ only in case, which is not supported:
/Views/Shared/Index.cshtml
/Views/Shared/Index.cshtml
Microsoft.AspNetCore.Mvc.Razor.Internal.RazorViewCompiler..ctor(IFileProvider fileProvider, RazorTemplateEngine templateEngine, CSharpCompiler csharpCompiler, Action compilationCallback, IList precompiledViews, ILogger logger)
Besides the problem that the view is ignored when I run from VS it's meaningful behavior. The app has two similar view and can't understand which one to choose.
So the question is how to force MVC to use a particular view (from app instead of from lib)?
There's a method to separate views with same relative names when they are belong to different controllers. Here's nice discussion - Restrict route to controller namespace in ASP.NET Core
But my case is a bit different.

How to create individual dll of asp.net mvc project pages? [duplicate]

I'm just learning asp.net mvc and I'm trying to figure out how to move my controllers into a separate project. Typically when I have designed asp.net web apps before, I created one project for my models, another for my logic, and then there was the web.
Now that I'm learning asp.net mvc I was hoping to follow a similar pattern and put the models and controllers each into their own separate projects, and just leave the views/scripts/css in the web. The models part was easy, but what I don't understand is how to make my controllers in a separate project be "found". Also, I would like to know if this is advisable. Thanks!
First of all, it is certainly a good idea to put your model into a separate project. As you've discovered, this is trivial.
Regarding Controllers and Views, I don't see any obvious advantage to separating them for most basic projects, although you may have a particular need to do so in a particular application.
If you do choose to do this, then you will need to tell the framework how to find your controllers. The basic way to do this is by supplying your own ControllerFactory. You can take a look at the source code for the DefaultControllerFactory to get an idea for how this is done. Subtyping this class and overriding the GetControllerType(string controllerName) method may be enough to accomplish what you're asking.
Once you've created your own custom ControllerFactory, you add the following line to Application_Start in global.asax to tell the framework where to find it:
ControllerBuilder.Current.SetControllerFactory(new MyControllerFactory());
Update: Read this post and the posts it links to for more info. See also Phil Haack's comment on that post about:
ControllerBuilder.Current.DefaultNamespaces.Add(
"ExternalAssembly.Controllers");
...which is not a complete solution, but possibly good enough for simple cases.
While it is reasonable to create your own ControllerFactory, I found it more convenient to define all my Controllers in each project, but derive them from Controllers in my Shared project:
namespace MyProject1.Controllers
{
public class MyController : MySharedProject.Controllers.MyController
{
// nothing much to do here...
}
}
namespace MySharedProject.Controllers
{
public abstract class MyController : System.Web.Mvc.Controller
{
// all (or most) of my controller logic here...
}
}
This has the added benefit that you have a place to put your Controller logic that differs from project to project. Also, it is easier for other developers to quickly find your Controller logic because the Controllers exist in the standard place.
Regarding whether this is advisable, I think it absolutely is. I've created some common Account Management logic that I want to share between projects that otherwise have very different business logic. So I'm sharing my Account and Admin Controllers, but the other Controllers are specific to their respective projects.
Add the Class Library for your mvc project.
In the class add the following code(For u'r Controller Code)
namespace ContactController
{
public class ContactController : Controller
{
public ActionResult Call()
{
ViewBag.Title = "Inside MyFirst Controller.";
return View();
}
}
}
On the mvc project view folder add the folder for Contact and create a Call.cshtml file.
Add the class library project reference into your main MVC project.
Finally to refer contact controller namespace into Route Config.
My problem solved after I updated System.Web.Mvc NuGet reference so MvcWebsite and Class Library use same System.Web.Mvc version
No need to add default namespaces
The simplest form of separation I use is to retain the Views "as is" in the original MVC project but remove the Controllers. Then in a new ClassLibrary project add the Controller classes and ensure they inherit from Controller.
The MVC routing engine will automatically route to the Controllers in the ClassLibrary and the Controllers will automatically construct the Views from the original MVC project, provided you have your references and usings correctly in place.
I am using this architecture to implement an Html Reports module that can be compiled and deployed separately from the main solution. At last I am free from SSRS!

Setting project url in VS2015 ASP.NET 5 Web API application

I'm trying to create a Web API project and a client-side web project, where the web project can access the API via ajax. Currently my project looks like this:
I saw this answer on here: Setting app a separate Web API project and ASP.NET app, which explains how the project url can be set to localhost:[port]/api.
But for ASP.NET 5 projects, the properties only have 3 tabs (as opposed to the several found in ASP.NET 4 projects):
What I'm wondering is:
Do I have to set this option somewhere else? (i.e project.json)
How would this work when I publish? Ideally I'd want [websiteURL]/api to serve up my API, whereas that link explicitly put localhost:8080.
Is having these as two projects a good idea? I could easily put API and web in the same project, but I like the separation of client-side and server-side logic.
Any help would be appreciated!
First Point:
Generally speaking in ASP.NET 5, the routing defaults are very good and should work out of the box without much in the way of configuration. You can use configuration and/or attribute based routing in your application (with a detailed overview of both here), although my personal preference is for the attributed approach. Provided you have the following line in your Startup.cs file (which you should have in a new project):
app.UseMvc();
you should be able to route requests to your api controllers in the fashion required (i.e. "/api/...") simply by using [Route] attributes as below (example taken from a standard generated ASP.NET 5 Web API application)
[Route("api/[controller]")]
public class ValuesController : Controller
{
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
The above example will route any GET request made to "/api/values".
While this approach can be used to handle requests made to your api, in order to deliver the files needed for your front end javascript application/single page app, you will need to enable static file serving. If you add the following to the Configure method in your Startup.cs class:
app.UseStaticFiles();
this will allow your application to serve those static files - by default, these are served from the ‘wwwroot’ folder, although this can be changed in the project.json file if required. The files needed for your front end app should then be added to this folder. A tutorial on serving static files can be found here.
Second Point:
In my experience this will not be an issue when you publish your website - provided your server is set up correctly, you will not need to include the port when making a request - navigating to [yourwebsitename]/api/... will suffice.
Third point:
In my opinion this entirely depends on how large the project is likely to grow, although preference and opinion will vary from developer to developer. Generally speaking, if the project will remain small in scope then keeping both in a single project is perfectly ok, as unnecessary complexity is reduced. However it is also very useful as you have pointed out, to maintain a separation of concerns between projects. So aside from the organisational advantage of your approach, the respective dependencies of the two projects are/will be kept separate also.

Adding Areas to a hybrid MVC-Webforms application

I am adding MVC support to a legacy ASP.NET Website project. I have added a separate class library for the Controllers and the Views are in the legacy app. It works great so far, but I'd like to take advantage of Areas. Since Areas encapsulate Controllers, Views and Models, if i add a new class project to host the Areas then would it work with the Views in this project?
Also, Areas need a web.config file and I assume I cant add a web.config to a class library Areas project.
Is this even possible?
I don't think the View files can be placed outside of the MVC3 project (it has to know where to find the files).
You can, however, specify a namespace for your Area routes that specifies where to search for your Controllers (so your controllers can remain in a separate project).
context.MapRoute("routename",
"{controller}/{action}",
new { controller="Home",action="Index"},
null,
new[] {"ControllerProject.NamespaceTo.Controllers"}
);
You will need to be sure you reference the proper ProjectTypeGuids in your project file.
http://www.kevinrohrbaugh.com/blog/2009/7/17/demystifying-the-aspnet-mvc-project-type.html
Be sure you have added the proper references for MVC as well. When all is configured, visual studios should allow you to right click the project and add areas with the supporting structure.

Segregating to 3 tiers from existing ASP.NET project

In my asp.net project currently i have business logic and and data access code in two sub folders(BLL,DAL) which are itself located web site project's app_code folder. I need to segregate them to two separate projects(one project for business layer and one project for Data access code).
How can I maintain connection strings necessary to Data access project which are currently in web.config file?(i.e if I choose Class library template for creating DAL and BLL projects)
How can I maintain various other web.config key values that are currently used in BLL, DAL code files?
How can I deploy compiled project? (ie Web site project I am currently deploying bin folder to Staging> production but this way where should i put DAL.dll and BLL.dll and relevant config files)
1 and 2) Add a 'using System.Configuration' and just reference them. Since their referenced in the project, asp.net will pick it up.
For example:
using System.Configuration;
namespace DataLayer
{
public class BaseDataAccess
{
public static string ConnectionString_Logging
{
get
{
return ConfigurationManager.ConnectionStrings["ConnectionString_Logging_Legacy"].ToString();
}
}
}
}
3) If properly referenced, upon compile, your BLL and DAL dlls will be in your bin folder of the main/ui project. If using web.config, your good to go.
Fundamentally, you should be wrapping those configuration bits up in objects along the way. But in any case, you can move them to a different class project without worry here -- it will pick up the configuration settings from whatever project it is hosted by, so you don't need to somehow provide the configuration to your library.
Your existing code should work, as the settings are read from the Config file of the running process, in this case your Web.Config, however i suggest you use custom configuration settings, these would be read from your Web.Config file, a typical implementation could look something like :
<YourCompany>
<YourCompany.ProjectName>
<Data ConnectionName="NameOfConnectionToUse" SomethingElse="XZY" />
<Business SomeValue="12345" />
</YourCompany.ProjectName>
</YourCompany>
Without getting into ideal settings/custom config etc, as asked - during runtime, your class libraries will get the configuration from the web.config if referenced as such from within these layers with no change. System.Configuration.AppSettings/ConnectionStrings will still work.

Resources