natemcmaster / DotNetCorePlugins

.NET Core library for dynamically loading code

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

View not found in a .net 5 web mvc application using the mvc sample plugin

davidbuckleyni opened this issue · comments

commented

I am using .net 5 web site mvc to build a wms system with plugins and I am using your plugin for that abilties to make it easier to consume the plugins however the view is just not being hit.

This is my full startup.css I have also created a directory called plugins and made sure it copied to the bin directory.
One thing I did was test with the sample you had so I copied MvcAppPlugin1 and copied it to my bin \ plugins directory,
However when I look at the goto the MyPlugin action ie localhost/MyPlugin nothing is display i logged into my system as it uses authorisation so i no nothing is going wrong that way.

I noticed when I compiled the app it created to binaries should the view be embbed so it doesnt produce the second dll.

image

public class Startup {
       private string _crmAPiKey = null;
       private string extensionsPath;

       public Startup(IWebHostEnvironment webHostEnvironment, IConfiguration configuration) {
           Configuration = configuration;
           this.extensionsPath = webHostEnvironment.ContentRootPath + configuration["Extensions:Path"];

       }
  
       public IConfiguration Configuration { get; }

       // This method gets called by the runtime. Use this method to add services to the container.
       public void ConfigureServices(IServiceCollection services) {
           services.AddLocalization(options => options.ResourcesPath = "Resources");
           _crmAPiKey = Configuration["CrmApiKey:ServiceApiKey"];
            
           services.AddSingleton<LocalizationService>();

    
           services.AddDbContext<WarehouseDBContext>
       (options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")), ServiceLifetime.Transient);
           services.AddRepository();

           services.AddIdentity<ApplicationUser, IdentityRole>(config => {
               config.SignIn.RequireConfirmedEmail = true;
               config.Tokens.AuthenticatorTokenProvider = TokenOptions.DefaultAuthenticatorProvider;
               config.User.RequireUniqueEmail = true;

           })
           .AddDefaultUI()


          .AddRoles<IdentityRole>()
          .AddDefaultTokenProviders()
          .AddEntityFrameworkStores<WarehouseDBContext>();
               services.AddControllersWithViews()
           .AddDataAnnotationsLocalization();
           services.Configure<CookiePolicyOptions>(options => {
               options.CheckConsentNeeded = context => true; // consent required
               options.MinimumSameSitePolicy = SameSiteMode.None;
           });
           services.AddSession(opts => {
               opts.Cookie.IsEssential = true; // make the session cookie Essential
           });

           services.AddRazorPages()
           .AddSessionStateTempDataProvider();

           services.AddMvc().AddNToastNotifyToastr(new ToastrOptions() {
               ProgressBar = false,
               PositionClass = ToastPositions.TopRight,
               ToastClass = "alert",

           });

           services.Configure<RequestLocalizationOptions>(options => {
               var supportedCultures = new[]
               {
                   new CultureInfo("en-US"),
                   new CultureInfo("en-GB"),
                   new CultureInfo("es"),
                   new CultureInfo("fr")
                   };

               options.DefaultRequestCulture = new RequestCulture(culture: "en-GB", uiCulture: "en-GB");
               options.SupportedCultures = supportedCultures;
               options.SupportedUICultures = supportedCultures;
               options.RequestCultureProviders = new List<IRequestCultureProvider>
       {
             new QueryStringRequestCultureProvider(),
             new CookieRequestCultureProvider()
             };
           }).AddControllersWithViews()

               .AddViewLocalization(LanguageViewLocationExpanderFormat.SubFolder)
   .AddViewLocalization(options => options.ResourcesPath = "Resources")
               .AddDataAnnotationsLocalization(options => {
                   options.DataAnnotationLocalizerProvider = (type, factory) => {
                       var assemblyName = new AssemblyName(typeof(SharedResource).GetTypeInfo().Assembly.FullName);
                       return factory.Create("SharedResource", assemblyName.Name);
                   };
               });

           services.AddControllers(config => {
               // using Microsoft.AspNetCore.Mvc.Authorization;
               // using Microsoft.AspNetCore.Authorization;
               var policy = new AuthorizationPolicyBuilder()
                                .RequireAuthenticatedUser()
                                .Build();
               config.Filters.Add(new AuthorizeFilter(policy));
           });

           services.Configure<IdentityOptions>(options => {

               // Default Lockout settings.
               options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
               options.Lockout.MaxFailedAccessAttempts = 5;
               options.Lockout.AllowedForNewUsers = true;


           });
           services.ConfigureApplicationCookie(config => {
               config.Cookie.Name = "Identity.Cookie";
               config.LoginPath = "/Identity/Account/Login/";
           });
           //services.AddTransient<MISDBContextSeedData>();
           services.AddSingleton<ISharedResource, SharedResource>();
           // using WebPWrecover.Services;
           services.AddTransient<IEmailSender, EmailSender>();
           

           services.Configure<AuthMessageSenderOptions>(Configuration);

           services.AddAutoMapper
(typeof(AutoMapperProfile).Assembly);


           services.AddAuthorization(options =>
     options.AddPolicy("TwoFactorEnabled",
         x => x.RequireClaim("amr", "mfa")));
           services.AddAntiforgery(o => o.HeaderName = "XSRF-TOKEN");
           var mvcBuilder = services
        .AddMvc()
        .SetCompatibilityVersion(CompatibilityVersion.Version_3_0);

           foreach (var dir in Directory.GetDirectories(Path.Combine(AppContext.BaseDirectory, "plugins")))
           {
               var pluginFile = Path.Combine(dir, Path.GetFileName(dir) + ".dll");
               // The AddPluginFromAssemblyFile method comes from McMaster.NETCore.Plugins.Mvc
               mvcBuilder.AddPluginFromAssemblyFile(pluginFile);
           }
           services.AddAuthentication(options => {
               options.DefaultAuthenticateScheme = IdentityConstants.ApplicationScheme;
               options.DefaultChallengeScheme = IdentityConstants.ApplicationScheme;
               options.DefaultSignInScheme = IdentityConstants.ExternalScheme;
           });
       }

       // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
       public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
           var locOptions = app.ApplicationServices.GetService<IOptions<RequestLocalizationOptions>>();
           app.UseRequestLocalization(locOptions.Value);

  //         seeder.SeedAdminUser();//this is my super user account do not over write
    //       seeder.SeedUser1();
      //     seeder.SeedUser2();
        //   seeder.SeedUserManager();

           var supportedCultures = new[]
           {
       new CultureInfo("en-US"),
       new CultureInfo("en-GB"),
       new CultureInfo("es"),
       new CultureInfo("fr")
       };
           app.UseRequestLocalization(new RequestLocalizationOptions() {
               DefaultRequestCulture = new RequestCulture("en-GB"),
               SupportedCultures = supportedCultures,
               SupportedUICultures = supportedCultures
           });
           if (env.IsDevelopment()) {
               app.UseDeveloperExceptionPage(new DeveloperExceptionPageOptions() { SourceCodeLineCount = 30 });
               app.UseDatabaseErrorPage();
           } else {
               //    app.UseExceptionHandler("/Home/Error");
               // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
               app.UseHsts();
           }
            app.UseCookiePolicy();

           app.UseNToastNotify();

           app.UseSession();
           app.UseHttpsRedirection();
           app.UseStaticFiles();
           app.UseRouting();
           app.UseAuthentication();
           app.UseAuthorization();
           


           app.UseEndpoints(endpoints => {
               endpoints.MapControllerRoute(
                   name: "default",
                   pattern: "{controller=Home}/{action=Index}/{id?}");
               endpoints.MapRazorPages();
           });
         
       }
   }

I'm not sure what's wrong. Perhaps something changed in .NET 5. Marking as help-wanted. If someone can identify the root cause and propose a fix, I'd appreciate the help.

The only way i found to use views for a MVC plugin in .NET5 is:

  1. Reference Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation in the main project:
    <PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="5.0.1" />

  2. Use .AddRazorRuntimeCompilation() in the mvcBuilder:

var mvcBuilder = services.AddControllersWithViews()
                           .AddRazorRuntimeCompilation();
  1. Use .AddRazorOptions to add my custom path for resolving views:
foreach (var dir in Directory.GetDirectories(_extensionsPath))
{
	var pluginFile = Path.Combine(dir, Path.GetFileName(dir) + ".dll");
	mvcBuilder.AddPluginFromAssemblyFile(pluginFile);

	mvcBuilder.AddRazorOptions(o =>
	{
		o.ViewLocationFormats.Add("/Plugins/" + new DirectoryInfo(dir).Name + "/Views/{1}/{0}.cshtml");
		o.ViewLocationFormats.Add("/Plugins/" + new DirectoryInfo(dir).Name + "/Views/Shared/{0}.cshtml");
	});
}
  1. Configure the plugin project to copy your views in the outputpath:
<ItemGroup>
	<Content Include="Views\**\*.cshtml">
		<CopyToOutputDirectory>Always</CopyToOutputDirectory>
	</Content>
</ItemGroup>

With these steps, you also have full access to views, components, PartialViews, ecc... from the plugin in your main project, for example:

@await Html.PartialAsync($"~/Plugins/PluginName/Views/Shared/view.cshtml"

@GiampaoloGabba can you make a sample repo, i tried this but its not working for me... weirdly i dont need it for a single plugin host application but all other plugins view are called then i get this error InvalidOperationException: Cannot find compilation library location for package 'System.LoginModule' which is the first module, it does hits the desired view on debug but afterwards breaking like this... any suggestion?

commented

I already closed this

@davidbuckleyni were you able to solve it?

commented

@davidbuckleyni were you able to solve it?

I actually ended up downloaded the code for SimplComerce it highlights allot of code that is missing from this example.

This issue has been automatically marked as stale because it has no recent activity. It will be closed if no further activity occurs. Please comment if you believe this should remain open, otherwise it will be closed in 14 days. Thank you for your contributions to this project.

Closing due to inactivity.
If you are looking at this issue in the future and think it should be reopened, please make a commented here and mention natemcmaster so he sees the notification.