Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP .NET Core MVC 2.1 mvc Views in plugin

I have a problem that has tormented me for several days.

As I do in a plugin in ASP .NET Core MVC to bring me views with the plugin

I have this situation

solution:EVS

  • Controllers
    • ...
  • Views
    • ...
  • Plugins
    • this folder contain a plugins dll
  • other folders ...
  • IPlugin.cs
  • Program.cs
  • Startup.cs

file IPlugin.cs

...
using McMaster.NETCore.Plugins;

namespace EVS
{
    public interface IPlugin
    {
        string Name { get; }
        void Do();
        void BootReg();
        void BootExecute();
        void PluginConfigure(IApplicationBuilder app, IHostingEnvironment env);
        void PluginConfigureServices(IServiceCollection services);
    }

    public class PluginsManager
    {
        public List<PluginLoader> PluginLoaders;

        public Dictionary<string, IPlugin> Plugins;
        public Dictionary<string, Assembly> Views;
        public List<String> dllFileNames;

        public PluginsManager()
        {
            PluginLoaders = new List<PluginLoader>();
            Plugins = new Dictionary<string, IPlugin>();
            dllFileNames = new List<string>();
            string PloginDirectory= Path.Combine(AppContext.BaseDirectory, "Plugins");
            if (Directory.Exists(PloginDirectory))
                dllFileNames.AddRange(Directory.GetFiles(PloginDirectory+"\\", "*.dll"));

            foreach (string dllFile in dllFileNames)
            {
                if (!dllFile.Contains(".Views.dll"))
                {
                    var loader = PluginLoader.CreateFromAssemblyFile(dllFile,sharedTypes: new[] { typeof(IPlugin) });
                    PluginLoaders.Add(loader);               
                }
                else
                {
                    // 

                    //
                }   
            }
            foreach (var loader in PluginLoaders)
            {
                foreach (IPlugin plugin in loader.LoadDefaultAssembly().GetTypes().Where(t => typeof(IPlugin).IsAssignableFrom(t) && !t.IsAbstract).Select((x)=> (IPlugin)Activator.CreateInstance(x)))
                    if (!Plugins.ContainsKey(plugin.Name))
                        Plugins.Add(plugin.Name, plugin);
            }
        }
    }
}

file Program.cs

namespace EVS
{
    public class Program
    {
        public static void Main(string[] args)
        {
            foreach (IPlugin plugin in Global.Static.PluginsManager.Plugins.Select((x) => x.Value))
                plugin.BootReg();           

            CreateWebHostBuilder(args).Build().Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>();
    }
}

file Startup.cs

namespace EVS
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.Configure<CookiePolicyOptions>(options =>
            {
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });


            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

            foreach (IPlugin plugin in Global.Static.PluginsManager.Plugins.Select((x) => x.Value))
            {
                plugin.PluginConfigureServices(services);
            }                
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }

            app.UseStaticFiles();
            app.UseCookiePolicy();

            foreach (IPlugin plugin in Global.Static.PluginsManager.Plugins.Select((x) => x.Value))
            {
                plugin.PluginConfigure(app,env);
            }

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

now let's take an example of plugins

solution:EVS.TestPlugin

  • Controllers
    • TestPluginController.cs
  • Views
    • test.cshtml
  • TestPlugin.cs

file: TestPlugin.cs

namespace EVS.TestPlugin
{
    internal class TestPlugin : IPlugin
    {
        public string Name
        {
            get
            {
                return "TestPlugin";
            }
        }

        public void BootReg()
        {
            ...
        }

        public void BootExecute()
        {
            ...
        }

        public void Do()
        {

        }


        public void PluginConfigure(IApplicationBuilder app, IHostingEnvironment env)
        {
            app.UseMvc(routes =>
            {
                routes.MapRoute("TestPlugin", "TestPlugin/", new { controller = "TestPlugin", action = "index" });
                routes.MapRoute("TestPlugint1", "TestPlugin/t1", new { controller = "TestPlugin", action = "t1" });
            });
        }

        public void PluginConfigureServices(IServiceCollection services)
        {

            services.AddMvc().AddApplicationPart(typeof(TestPluginController).GetTypeInfo().Assembly).AddControllersAsServices();
        }

    }
}

file: Controllers/TestPluginController.cs

namespace EVS.TestPlugin.Controllers
{
    public class TestPluginController : Controller
    {
        public IActionResult Index()
        {
            return Content("<h1>TestPluginController</h1>");
        }

        public IActionResult t1()
        {
            return View("test");
        }
    }
}

file : Views/test.cshtml

@{
    Layout = null;
}

`.cshtml` test view

Plugins are not included in the solution, but they are loaded dynamically depending whether they are in a folder solemnly for them.

The problem: I can accordingly add controllers as (EVS.TestPlugin.PluginConfigureServices (...)) and MapRoute (EVS.TestPlugin.PluginConfigure (...)) However, how could I also add the context of the views? Therefore it would be on: ~/PluginName/Views/ViewName.cshatml

EDIT

I found this (link), but it's not really what I need. It partially solve the problem, because it only work if the view are compiled.

EDIT 2

Momentarily I solved it adding the views reference on to the plugins csproj, as follows:

<ItemGroup>
    <EmbeddedResource Include="Views\**\*.cshtml"/>
    <Content Remove="Views\**\*.cshtml" />
</ItemGroup>
<PropertyGroup>
    <RazorCompileOnBuild>false</RazorCompileOnBuild>
</PropertyGroup>

On the source project:

// p is a Instance of plugin
foreach(IPlugin p in Plugins.Select((x) => x.Value))
    services.Configure<RazorViewEngineOptions>(options =>
    {
        options.FileProviders.Add(
                    new EmbeddedFileProvider(p.GetType().GetTypeInfo().Assembly));
    });

This does not solve my problem because the .cshtml files come in clear in the plugin file, what I need is to be able to add the views from the assembly pluginname.views.dll or in some other way, the important that the views are compiled

EDIT 3

Good news, using ILSpy I parsed a PluginName.Views.dll file and I discovered that it implements RazorPage<object>

With the following code I verified that I could initialize all the caches that cono RazorPage<object> which allows me to have the instances of the objects that create the view

foreach (string dllview in Views.Select((x) => x.Key))
{
    PluginLoader loader = PluginLoader.CreateFromAssemblyFile(dllview, sharedTypes: new[] { typeof(RazorPage<object>) });
    foreach (RazorPage<object> RazorP in loader.LoadDefaultAssembly().GetTypes().Where(t => typeof(RazorPage<object>).IsAssignableFrom(t)).Select((x) => (RazorPage<object>)Activator.CreateInstance(x)))
    {
        // i need a code for add a RazorPagein the in RazorViewEngine, or anywhere else that allows register in the context

        System.Diagnostics.Debug.WriteLine(RazorP.GetType().ToString());
        /*  - output 
            AspNetCore.Views_test
            AspNetCore.Views_TestFolder_htmlpage

            as you can see is the folder structure

            - folder tree:
            Project plugin folder
                Views
                    test.cshtml
                    TestFolder
                        htmlpage.cshtml

        */
    }
}

Solved

thanks to all I solved, there seems to be a problem with

var assembly = ...;
services.AddMvc()
    .AddApplicationPart(assembly)

someone forgot to even put the razzor part factory

CompiledRazorAssemblyApplicationPartFactory

I solved that

services.AddMvc().ConfigureApplicationPartManager(apm =>
    {
    foreach (var b in new CompiledRazorAssemblyApplicationPartFactory().GetApplicationParts(AssemblyLoadContext.Default.LoadFromAssemblyPath(".../ViewAssembypath/file.Views.dll")))
        apm.ApplicationParts.Add(b);
    });

in addition I published a library that does everything efficiently

NETCore.Mvc.PluginsManager

like image 607
Gianfrancesco Aurecchia Avatar asked Oct 09 '18 13:10

Gianfrancesco Aurecchia


1 Answers

thanks to all I solved, there seems to be a problem with

var assembly = ...;
services.AddMvc()
    .AddApplicationPart(assembly)

someone forgot to even put the razzor part factory

CompiledRazorAssemblyApplicationPartFactory

I solved that

services.AddMvc().ConfigureApplicationPartManager(apm =>
    {
    foreach (var b in new CompiledRazorAssemblyApplicationPartFactory().GetApplicationParts(AssemblyLoadContext.Default.LoadFromAssemblyPath(".../ViewAssembypath/file.Views.dll")))
        apm.ApplicationParts.Add(b);
    });

in addition I published a library that does everything efficiently

NETCore.Mvc.PluginsManager

like image 55
Gianfrancesco Aurecchia Avatar answered Oct 21 '22 02:10

Gianfrancesco Aurecchia