How to embed view files in assemblies using Cofoundry - cofoundry

Is there any Cofoundry specific solution to embedding views in assemblies and use these views by the main website assembly?
For instance I have /Views/CofoundryPages/Components/BookingRequest/Default.cshtml which is the view for a ViewComponent that belongs to a separate assembly "Bookings.dll". But I have to leave the cshtml view file in the main website assembly "Website.dll".
Is there a standard way to embed my Default.cshml file in "Bookings.dll" and still have the Cofoundry framework pick it up in its virtual path provider (or what ever it is)?

Yes, you can use the modular system that Cofoundry itself uses to bootstrap the admin panel and for plugins. There's a couple of things to consider here:
Register your Assembly
To register your assembly with Cofoundry, simply add a class that implements IAssemblyResourceRegistration. This is a simple marker class that gets picked up by the DI system and registers it with the Cofoundry AssemblyResourceProvider so your views can be found. E.g.
public class ExampleAssemblyResourceRegistration : IAssemblyResourceRegistration
{
}
Ensure your assembly is scanned by Cofoundry
Your assembly will be automatically scanned by the Cofoundry DI system if it follows the default rules, otherwise you may need to implement an IAssemblyDiscoveryRule and register it at startup. More information in the Cofoundry DI docs

Related

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

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.

where should inheritance classes be stored?

I want to create classes that can be inherited by other classes to use along my project.
i.e. i want to create a separate class for each Mail method, each class will implement differently the Compose method, and more Mail classes will be created in the future.
My question is - Where should I create these classes/interfaces? in the App_Code ?
I would suggest creating a separate assembly and include that assembly as a reference in your Web Project. That will enable you to write independent test cases against your classes / interfaces.
Other than user controls you can store source files in App_Code. In your case you can store Interfaces/classes in App_Code. You can have multiple folders for group of class/interfaces inside the AppCode.
AppCode
You can store source code in the App_Code folder, and it will be
automatically compiled at run time. The resulting assembly is
accessible to any other code in the Web application. The App_Code
folder therefore works much like the Bin folder, except that you can
store source code in it instead of compiled code. The App_Code folder
and its special status in an ASP.NET Web application makes it possible
to create custom classes and other source-code-only files and use them
in your Web application without having to compile them independently.
Edit: You may see this discuss: How to organize ASP.NET app_code folder?

Can I push webforms pages and/or other content into another assembly outside of the webapp project?

I would like to start pulling out pages that meet certain criteria from the main assembly into a referenced assembly, while we transition the project to MVC.
Alternatively, I could create a fresh MVC project and just reference this assembly's pages/static content if that is possible. How would I go about it either way?
I'm thinking legacy .js,.css static content.
Also aspx, aspx.cs, ascx.cs, ascx.designer.cs, aspx.designer.cs and ascxpages that are functionally ready to be obsoleted for mvc .cshtml versions.
Oh and ESPECIALLY all references to 3rd party User controls.
Not 100% confident this will help, but in short I would ask:
Why don't you pull the logic of the Handler, WebPage into another class library and in aspx/ahsx header set the referencing class to the class library's namespace. If the class library is in your bin (referenced) I don't see a problem.
With static content, I don't see why you would move it to another assembly as it is not in an assembly to start with (served by IIS). If you must move it you will need to write a proxy class to make the needed io to read the file(s) and send to the response stream.

ASP.NET page or control marked as internal doesn't work and gives "inaccessible due to its protection level" error

I know why this happens and I want to find a workaround it.
Example: a user control that exist in 2 assemblies, loaded at the same time.
namespace MyNamespace
{
internal class MyUserControl : UserControl
{
}
}
My situation - I want to be able to share user interface control between two web applications which are loaded at the same time in the same application pool. If my user interface class is marked public, then ASP.NET will not like it because it will be duplicated into memory ( CLR uses namespaces as full qualifier for class, and if the same class and namespace is met it more than one assembly, exception is thrown ). I do then mark the class as internal and of course I forget how UI compilation occurs, and I expect it to work. Of course it doesn't because ASP.NET compiles the UI into separate assembly which is dynamic and since I marked the class as internal, it is not visible to the main assembly...
The question that follows is: How can I make dynamic compiled assemblies that ASP.NET generates to view the main application assembly internals? It is very unfortunate that classes and methods I write into my UI controls must always be public! Has anyone met this obstacle in his daily work and found a workaround?
EDIT:
Precompilation is not possible here due to other circumstances.
You can share User Control markup between apps using a Virtual Path Provider (see
http://weblogs.asp.net/scottgu/archive/2005/11/27/431650.aspx and http://msdn.microsoft.com/en-us/library/system.web.hosting.virtualpathprovider.aspx).
You can pull the markup for the control from anywhere - SharePoint uses VPP to get markup from the database, and I use it in some projects to pull from a non-standard location on disk (which is shared between projects).
If your base class is internal, the derived class that ASP.NET generates at runtime will not be able to extend it.
However, I don't understand the issue that you're running into in the first place. Two distinct web applications will always run in different AppDomains (even if they're in the same app pool), and there should be no type conflicts between the AppDomains.
I probably need more details about what you are doing. e.g. what assembly are you compiling your base class into, and why does it end up in two different assemblies?

Does App_Code in ASP.Net compile new code to a secondary temp AppDomain?

I am trying to better understand how the App-Code folder operates, and in particular I am curious how new objects that a new class in the App-Code folder are included in the current AppDomain. Is a second, temporary AppDomain created in the same manner as when I compile new objects with the CodeDom?
In many ways the capability of moving new classes / objects into a Web Application is very compelling. Rob Connery's MVC Storefront is a good illustration. If you have used the App-code folder to deploy new functionality were there any trade-offs or gotchas that should be considered?
UPDATE:
I found an article in CoDe Magazine with this interesting passage:
Your application-specific code can go
inline of the ASPX page or control, it
can go into a CodeBeside partial
class, or you can create completely
autonomous classes in the APP_CODE
folder. The APP_CODE folder is a
special folder in an ASP.NET 2.0
project and any non-page or
control-related source code in your
Web project must go into this folder.
ASP.NET treats the content of APP_CODE
like a library project and compiles
the content into a separate assembly.
This assembly is then referenced by
all of the page or directory-level
assemblies that ASP.NET creates from
your ASPX/ASCX pages that use any of
the classes defined in APP_CODE.
My question still stands - is a second AppDomain created that supports this libary, and if so are the impacts on performance minimal?
I don't know the details, but it will certainly be in different dynamic assemblies. A class can't be in another AppDomain. Only an instance of a class can be in another AppDomain, and then it's really a hassle to access from another AppDomain (only through remoting)

Resources