How to reference to assembly in mvc at runtime
Asked Answered
A

3

25

In my Asp.Net MVC application, i have some view file (.cshtml) which has reference to an external library which it will be loaded at runtime. so after app started, i load the assembly by Assembly.Load and i register the controllers by my own custom ControllerFactory and every thing is ok.

But, in some views which has references to the dynamically loaded assembly, throws the :

Compiler Error Message: CS0234: The type or namespace name 'MyDynamicNamespace' does not exist in the namespace 'MyApp' (are you missing an assembly reference?)

exception that tells the razor compiler cannot resolve the related assembly.

My question is that, is there a way to register the assembly at runtime, to able the razor compiler can access to it and resolve it?

Notice that i can't use BuildManager.AddReferencedAssembly method because my assembly have to be loaded after app start, and the BuildManager does not support it.

Artifice answered 28/6, 2015 at 6:43 Comment(8)
I'm not clear why your requirement is that the assembly must be loaded after app start? That wouldn't be a normal use case for external libraries.Kalmuck
it is something like plugin, or think about modular web applications such as orchard cms. actually the assembly with its views comes at run-time which user gives! :)Artifice
I don't believe this is possible as when using roslyn in a dnx environment the dynamic compiler looks to resolve dependencies using the project.json as part of the build process. Your dynamically loaded assembly will not be listed in project.json and thus the compiler is going to fail. I guess in theory it would be possible to dynamically adjust the project.json prior to reflect the new dependency after start and prior to the view being loaded but honestly I have no idea if it would work and it would be a hack at best. Even if it did work it could break in the future.Jiffy
In looking to source code of the BuildManager, clearly it throws if app is not in pre-start phase and I can't figure out WHY! :(Artifice
If it's a compiler error stopping you perhaps you should try and removing any references to types in your view and use dynamic instead?Lattice
@Lattice thanks for your attention, Yes, you're right, It's compiler error, But NO :) it is not belong to the C# compiler!, It happens in the process of compiling assemblies and pages for application. in this phase the dynamic assembly is not accessible and like not referenced while it is referenced and accessible in application. (msdn.microsoft.com/en-us/library/…)Artifice
How does the parameters of your assemblys look? The first thing the compiler does is look in the GAC if the assembly has been previously resolved. What it looks for is the following matching parameters<reference include="MyAssembly, Version=2.1.0.0, Culture=neutral, PublicKeyToken=17fac983cbea459c" /> I want to exclude the possibility that you create your dynamically loaded assemblys with parameters that will make CLR fail its lookup.Bunns
@MagnusKarlsson, Thank for your efforts. I don't register it in GAC, I just load it to AppDomain and I know that it's not enough. but how i can add reference to GAC in remote server which often we don't have permission to do it. On the other hand I'm not sure about adding reference to web.config at run-time affect to BuildManager. and the next important is the assemblies which are loaded dynamically, are temporary and the UN-registering them is my second problem :)Artifice
T
9

1) I wouldn't recommend having your views directly use external references or dynamically loaded external references. Abstract this by having your view interact with a controller. Make your controller feed a data object to your view that is known at build time by your application (in other words, an object known to your web application at build time). This is to completely isolate (abstract) plugin specific business from your view. Then make your controller interact with the "plugin".

2) I don't know how your "custom factory" works but nowadays we don't really build any "custom factories" anymore. Instead we leverage dependency injection containers such as Microsoft Unity(or Ninject, or Castle Windsor or etc..). Creating "custom factories" is very old fashioned and you're basically reinventing the wheel that has been solved with dependency injection.

3) As far as dynamically loading external assemblies, I don't know if you have it right but here's a link:

Dynamically load a type from an external assembly

4) Typically, a plugin design exposes interfaces that are known to your main web application at build time. What the plugin design hides is the implementation which can change from one plugin to another. The important thing is that each plugin implements the same public interfaces, those that are expected by your main web app. Usually, you will have those interfaces in a separate "Common" project that is referenced by both, your main web application and your plugin that implements those interfaces. Therefore, from your main web app, you will know what the public interfaces of your plugins are, you can dynamically load the external assembly and use C# reflection to find the classes that implements those interfaces and load them into your dependency injection container. Likewise, anyone who will want to develop of a plugin for your web app will have to implement the interfaces that are defined in your "Common" project.

Note: "Common" is just a random name I gave to the project. You can name it "PluginInterface" or whatever you want.

After that, having your controller grab whatever it needs from the dependency injection container is trivial.

Note: Your plugin interfaces will probably have input and output entities. These entities are shared between your main web app and your plugin. In such case, since these entities are part of your interfaces they need to be in the "Common" project. You may be tempted to have your controller return those entities directly to your view but then you won't have a perfect abstraction between your view and your plugin. Not having perfect abstractions is for another discussion.

Hope it helps!

Tsuda answered 28/6, 2015 at 16:52 Comment(4)
Your 3 doesn't make sense in this situation, since this question is based on the assumption that you need to influence the asp.net dynamic build process.Heroism
The OP's question is about the "plugin design pattern" and I provided information on the right way to do it. It has nothing to do with "ASP.NET dynamic build process". I stand by my answer. The OP hoped that he could use BuildManager to implement his plugin design pattern but as he has discovered it has nothing to do with that.Tsuda
@Tchi At first, thank you so much for your answer. There is good things but actually I bleive that the view of an action should be able to access to any namespaces which the action has, it seems be a normal expectation. Hovever i have a exprehensive DDD to provide commomn interfaces but this is not a good reason for emit any references inside a view to its plugin namespaces or services such as ViewModels, DataProviders, enums , ... :)Artifice
And, in my architecture I permit the plugins to reference just the core(DDD) and itself. NOT other plugin nor external library. And even if any plugin needs to talk with other plugins it will be done by indirect mechanism throgh the core as well as with IoC techniques.Artifice
R
7

As a Sys Admin, I would recommend a maintenance period, especially if the file you replace messes something else up. Even if your maintenance period is only a half hour it is good practice.

As for the DLL and recompile... typically the IIS Worker Process (the service running your application pool) will recycle at normal intervals based on the IIS configuration and memory usage. When this happens the application will recompile if anything requires the JIT. It also terminates all open user sessions as it physically stops and then re-starts. The worker process also monitors the root directory (like you mentioned) for any file changes. If any are found a recompile is forced. Just because a dependency is changed does not force a recompile. If you pre-compile your DLL the only thing left to compile is any code inside of your actual ASPX file and this uses the JIT which compiles each time. From what you described IIS should not need to recompile or restart, sounds like another problem where IIS is hanging when you swap out the file. Might need to get a sys admin involved to look at the IIS logs.

Good Luck!

http://msdn.microsoft.com/en-us/library/ms366723.aspx

http://msdn.microsoft.com/en-us/library/bb398860.aspx

Roosevelt answered 3/7, 2015 at 5:57 Comment(1)
Actually I'm restarting the IIS manually, but its not exactly what i want. I hope one day get the solution or someone help. Thank you @FarhadArtifice
A
2

Here is a note that may helps: If you are not loading your assemblies from the /bin directory, you need to ensure that the path to the assemblies is discoverable:

AppDomain.CurrentDomain.AppendPrivatePath(path_to_your-dyna_assembly);
Amnion answered 5/7, 2015 at 20:51 Comment(1)
Thank you for your attention Javad, but unfortunately it doesn't help me, ever the problem is not that. assembly is discoverable even they loads from a path which have been marked as <probing privatePath="***ThePath***" />. and in addition i have implemented an assembly resolver which works well for me. but when any page goes to be compiled the context of compiler (BuildManager) does not access or reference to the assembly.Artifice

© 2022 - 2024 — McMap. All rights reserved.