📜 ⬆️ ⬇️

ASP.NET Core RC2: Integrated Modularity Support (application parts)

Being historically immersed in the issues of developing modular applications on ASP.NET, the first thing I did when ASP.NET Core RC2 came out was to try to translate my ExtCore modular framework into it. And it turned out that in the new version everything changed and the old approaches from RC1 no longer work, but there were new interesting features, which I want to tell about.

In short, the development of modular applications in RC2 is very simplified. Thanks to the new feature “application parts” (application parts), you can easily divide your large project into several smaller ones and then freely assemble them. This is especially convenient when working with areas (areas), which already isolate a set of controllers, views and other resources - each area can now be divided into a separate project. As I understand it (in particular, from aspnet / Mvc # 4089 ), the implementation is focused on dividing a large project into small ones and only in MVC. The rest still have to write yourself.


Implementation


For example, create a small application and see how everything works (it is assumed that you have already come here and installed everything you need). So, create a project:
')


In the next step, select “Web application” so that Visual Studio creates an application that is ready for testing:



That's all. Now let's launch our new application:



I will not dwell on the structure of the project and on the differences in structure from what we are used to in RC1. If you wish, you can see this .

Now add another project to our solution, this time the class library:



Since we want to look at the work of the controller and the view from our assembly, we will add a link to MVC in the project.json file. We also need the views in this project to be added to the assembly as resources. This is done using the appropriate settings in the buildOptions section of the project.json file. As a result, we get the following file:

{ "buildOptions": { "embed": [ "Views/**" ] }, "dependencies": { "Microsoft.AspNetCore.Mvc": "1.0.0-rc2-final", "NETStandard.Library": "1.5.0-rc2-24027" }, "frameworks": { "netstandard1.5": { "imports": "dnxcore50" } }, "version": "1.0.0-*" } 


Now we will create in our project a new controller with a single method (for consistency, it is advisable to place the file with the controller class in the Controllers folder, although this is not necessary):

 public class ModuleAController : Controller { public ActionResult Index() { return this.View(); } } 


Now in the \ Views \ ModuleA folder we will create an Index.cshtml view with the content you like.

The project is ready. Let's collect it. In the folder with the project folder will appear (as in previous versions of ASP.NET), and in it - our assembly. It remains only to tell about it to the main application.

Open the Startup class of our application and go to the ConfigureServices method. First of all, let's load our build with the controller and view:

 Assembly assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(@"   "); 


Next, add the loaded assembly as part of the application in MVC:

 services.AddMvc().AddApplicationPart(assembly); 


Almost everything. If we start the application now and go to the address / modulea, we will get an exception: InvalidOperationException: The view 'Index' was not found. To explain MVC that you need to look for views inside our assembly, we will add the appropriate file provider in the Razor settings. Modify the previous line of code to make it like this:

 services.AddMvc().AddApplicationPart(assembly).AddRazorOptions( o => { o.FileProviders.Add(new EmbeddedFileProvider(assembly, assembly.GetName().Name)); } ); 


Our application, consisting of two parts, is ready. Run it, go to the address / modulea:



Very good. Back in RC1, this would require more code. But this is enough only as long as you do not want to use strongly typed representations. If we add a view model class to our project, and then specify it as a model for our view, we’ll get an exception at runtime: The AspNetCoreApplicationParts 'ModuleA' doesn’t exist. This is due to the fact that our assembly is not included in the assembly set, in which Razor is looking for types when compiling views. Fortunately, there is a fairly simple way to fix this. In addition, in the near future, this step will not be necessary, since assemblies added as parts of the application will automatically participate in Razor compilation.

We modify the call to the AddRazorOptions function that we used in the previous step, thus:

 .AddRazorOptions( o => { o.FileProviders.Add(new EmbeddedFileProvider(assembly, assembly.GetName().Name)); Action<RoslynCompilationContext> previous = o.CompilationCallback; o.CompilationCallback = c => { if (previous != null) { previous(c); } c.Compilation = c.Compilation.AddReferences(reference); }; } ); 


It remains to declare the reference variable somewhere before loading the assembly:

 PortableExecutableReference reference = MetadataReference.CreateFromFile(@"   "); 


That's all. Now we can use our view model. Run the application and go to the / modulea address:



By the way, even in RC1 it was possible to use a preliminary compilation of representations and not have problems with resolving the types of view models at run time. Unfortunately, preliminary compilation is not supported in RC2 (as far as I understood, due to the complexity of the implementation), but it will be returned in the future .

Result


Perhaps the application parts are exactly what ASP.NET has long lacked. I spent a lot of time to achieve a similar result in previous versions (even before ASP.NET Core). I hope this example is quite enough to start using this feature. And thanks to the guys from our chat in Gitter, with whom we dealt with RC2.

The entire project (in a slightly simplified form) is available on GitHub .

Source: https://habr.com/ru/post/301336/


All Articles