aspnet / Mvc

[Archived] ASP.NET Core MVC is a model view controller framework for building dynamic web sites with clean separation of concerns, including the merged MVC, Web API, and Web Pages w/ Razor. Project moved to https://github.com/aspnet/AspNetCore
Apache License 2.0
5.62k stars 2.14k forks source link

Find ASP.NET Core 2 Razor Pages in another assembly #6934

Closed xsoheilalizadeh closed 6 years ago

xsoheilalizadeh commented 7 years ago

Hi, I want to locate My Project Razor Pages in another assembly. for doing this I write following code:

        public void ConfigureServices(IServiceCollection services)
        {
            var adminAssembly = Assembly.Load(new AssemblyName("App"));
            services.AddMvc().AddApplicationPart(adminAssembly).AddRazorOptions(options =>
            {
                var previous = options.CompilationCallback;
                options.CompilationCallback = context =>
                {
                    previous?.Invoke(context);

                    context.Compilation = context.Compilation.AddReferences(
                        MetadataReference.CreateFromFile(typeof(dodo).Assembly.Location));
                };
            });

            services.Configure<RazorViewEngineOptions>(options =>
            {
                options.FileProviders.Add(new EmbeddedFileProvider(Assembly.Load("App")));
                options.FileProviders.Add(new PhysicalFileProvider(@"C:\Users\soheil\Documents\Visual Studio 2017\Projects\WebApplication5\App"));
            });
        }

my solution: devenv_2017-10-10_18-44-26

when running localhost:5000/SameTodo Get Followig Error:

One or more compilation references are missing. Ensure that your project is referencing 'Microsoft.NET.Sdk.Web' and the 'PreserveCompilationContext' property is not set to false.

stack:

The type or namespace name 'SameTodoModel' could not be found (are you missing a using directive or an assembly reference?)
+
        public global::Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper<SameTodoModel> Html { get; private set; }
The type or namespace name 'SameTodoModel' could not be found (are you missing a using directive or an assembly reference?)
+
        public global::Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary<SameTodoModel> ViewData => (global::Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary<SameTodoModel>)PageContext?.ViewData;
The type or namespace name 'SameTodoModel' could not be found (are you missing a using directive or an assembly reference?)
+
        public SameTodoModel Model => ViewData.Model;
The type or namespace name 'SameTodoModel' could not be found (are you missing a using directive or an assembly reference?)
+
        public global::Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary<SameTodoModel> ViewData => (global::Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary<SameTodoModel>)PageContext?.ViewData;

and set PreserveCompilationContext to false but now worked how can I solve this problem? also, ask this in StackOverflow

pranavkm commented 7 years ago

Where's the type SameTodoModel located? It doesn't look very clear if it's part of 'App'.

xsoheilalizadeh commented 7 years ago

located in App and Pages folder.

pranavkm commented 7 years ago

Does WebApplication5 reference App? I'm trying to figure out what you're doing to get it to load using Assembly.Load. In fact, if you could possibly share this as a single repo, that would certainly help.

pranavkm commented 7 years ago

Some notes from my investigation:

I tried adding a compile reference \ runtime reference to an assembly not referenced by the application i.e. wire up things so you could point to an arbitrary project on disk + associated views.

I started with Assembly.LoadFile to add an AssemblyPart. If the assembly has a co-located deps.json: (a) if it does have PreserveCompilationContext, resolving reference paths fail. DependencyModel does not look for binaries next to the deps file. (b) If we do disable PreserveCompilationContext, DependencyModel finds the deps file but has any no CompileLibraries to work with. It ends up returning an empty sequence. Consequently the added AssemblyPart does not in itself gets referenced during view compilation.

Setting GenerateDependencyFile seems to be the only good way to get it actually referenced.

Once you get past this, you start running in to issues where the load context in which we load and reflect over the generated \ compiled Razor type isn't the same as the load context of the AssemblyPart assembly. I tried using AppDomain.AssemblyResolve, but that didn't help either. The only way to get things working was to reference the second project at which point things just worked.

Speaking to @sebastienros, it looks like Orchard switched from attempting to load arbitrary assemblies to actual references. I'm not entirely sure if this scenario works E2E very well.

pranavkm commented 6 years ago

@SoheilAlizade feel free to reopen once you have a sample repro.

molaie commented 6 years ago

@pranavkm I have very same problem. i've created a repo on github Test Repo url Hope you can test and investigate on it. some more description: i'm using StructureMap for the DI part. the problem is that when in Yooshina.Host project, i add a reference to Yooshina.Modules.Core, there is no problem, but if i remove the direct reference and load it using reflection and other things, it gives the error: An error occurred during the compilation of a resource required to process this request. Please review the following specific error details and modify your source code appropriately. then i added <PreserveCompilationContext>false</PreserveCompilationContext> to Yooshina.Host.csproj, but this time i get the second error as in screenshot.

image

molaie commented 6 years ago

also created a gist for error: https://gist.github.com/molaie/788cb0279498c7efbbb4b751241d8896

rynowak commented 6 years ago

Reopening this since we have some information

acinep commented 6 years ago

I am hitting something similar when trying to self-host ASP.NET Core - with views/controllers in a separate assembly.

Full repro available here https://github.com/acinep/Repro/tree/master/Mvc_6934

pranavkm commented 6 years ago

@acinep your repro works fine for me i.e. I can see it print "Hello from embedded view" I clone and run. Can you try doing a clean build?

pranavkm commented 6 years ago

@molaie you are copying deps files along with your module binaries to the output directory. Deps files are looked up next to an assembly, but assemblies listed by it for purposes such as compilation are always resolved (a) the application's bin directory (b) the shared runtime (c) the runtime store. Consequently having the deps file in the modules directory prevents these assemblies from being resolved. Removing the deps file solves this immediate issue, but you're still going to run in to issues where you would have to manually resolve the transitive dependency graph of module dependencies as compilation references. I really wouldn't recommend going down this road.

In general, instead of rolling your module system, we recommend something more robust like the one Orchard Core provides. Orchard Core modules allow you to build modules as NuGet packages that can be added to your application on the fly. And you can use them as a low level framework without adopting the entire Orchard CMS infrastructure. Check out https://github.com/OrchardCMS/OrchardCore.Samples to see what this looks like.

@acinep if you're still seeing issues, feel free to open a new issue. I'm closing this since there isn't any action item for us to do.