I am attempting to use the Razor view engine as a general templating engine backed by a database.
http://razorengine.codeplex.com/ The problem is that for every template compilation a new dynamic assembly is created and loaded. As there is no way to unload an assembly from the current appdomain and no way to use a separate appdomain for the templating system (use of anonymous types) these assemblies will keep accumulating until the appdomain is destroyed. The templates themselves will change on a regular basis and as such will result in more recompiles.
The question is will these dynamic assemblies (potentially thousands) hurt the appdomain performance? Or alternately is there a better way to do this?
In general having many small assemblies loaded in the AppDomain shouldn't be something to worry about too much. The only general statement anyone could make about this is to measure the actual performance of the app in the relevant scenarios and then see if it matters.
ASP.NET has some automatic app lifecycle management that will recycle the AppDomain after certain events. For example, if there are too many recompilations in the app then ASP.NET will automatically restart the app. This means that all the previously loaded assemblies will be cleared out and you start from scratch.
See MSDN for more info: http://msdn.microsoft.com/en-us/library/s10awwz0.aspx
numRecompilesBeforeAppRestart
Optional Int32 attribute.
Specifies the number of dynamic recompiles of resources that can occur before the application restarts. This attribute is supported at the global and application level but not at the directory level.
Note
ASP.NET increases the NumRecompilesBeforeAppRestart property every time an assembly is invalidated and fails to be deleted.
The default is 15.
Related
I'm developing modular application using WPF and Prism.
All my UserControls have separate assemblies and implement IUserControl interface.
I would like to list all Types which implement IUserControl interface form a loaded module library in this way;
//ModuleA.cs
var interfaceType = typeof(IUserControl);
var userControlTypes = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => interfaceType.IsAssignableFrom(p) && p.IsClass);
But I cannot see all UserControl types implementing IUserControl in userControlTypes list.
When I use the all classes that implements IUserControl in Bootstrapper.cs like in the following;
var userControlTypes = new List<Type>()
{
{typeof(HastaKayitControl)},
{typeof(ViziteUserControl)},
{typeof(DenemeUserControl)},
...
};
I can get all desired UserControls from the list just I wrote above(userControlTypes).
What is the reason behind this?
FYI:
All assemblies target the same .NET framework version.
My Prism version is 6.1.0
I will use userControlTypes to show all UserControl types inside the application to the end-user.
IUserControl interface contains nothing.
This behavior is by design. The .net CLR will not load in an assembly unless it is called/entered which forces it to be loaded. Imagine the startup cost of running an app if every .dll file in the directory were loaded into memory when the application started as opposed to when a type was referenced at run time for the first time, some apps with large libraries would have load times of minutes (maybe even more?). Also it would not be realistic because some types are resolved to libraries outside of the execution folder like assemblies that resolve to the GAC.
In your first example AppDomain.CurrentDomain.GetAssemblies will only return the loaded assemblies, not all the assemblies, in that application domain. To see this you could add a {typeof(ViziteUserControl)} (taken from your next code part) and place it right above it, this will force the type (and containing assembly) to be loaded by the CLR and now it (types containing assembly) too will be returned by AppDomain.CurrentDomain.GetAssemblies.
In your next code fragment your code is explicitly entering these assemblies and adding the types. I do not think this requires any explaining.
So if you want AppDomain.CurrentDomain.GetAssemblies to load all your types across your application you need to force the assembly to load into memory if it has not already done so. Depending on your structure you could do this a couple of ways.
Iterate through the .dll files on disk (using a reference location like Assembly.GetExecutingAssembly.Location) and call Assembly.LoadFrom. Use wild cards to ensure you are only loading your assemblies and not every .dll library you are encountering.
Reference interested types in a configuration file and load them from there. You can use Type t = Type.GetType(yourConfigType); when creating your list of types from your configuration string list.
Reference interested assemblies in a configuration file and load in the DLL in the same manner as option 1.
Just hard code the list as you did in your last example.
If you choose option 1 or 3 you will have to check to make sure you have not already loaded the assembly in memory before you call Assembly.LoadFrom. You can do this by again checking what is already loaded with AppDomain.CurrentDomain.GetAssemblies().Any(x =>your search query).
Also Note that once you load an assembly into your application domain you cannot unload it for the life of that application domain. If you do not want this but you still want to dynamically find all your types you will have to create a 2nd application domain to find all the types and return them as an array/list of fully qualified type name as a string. You can then unload this created application domain. Also, as correctly noted by #Peter below in the comments, use ReflectionOnlyLoadFrom if you go with this approach. This incurs much less overhead.
AppDomain.GetAssemblies() tells you the loaded assemblies, not the referenced ones. I can't speak to the Prism aspect of your question, and I agree with the comments that there is probably a better way to design this. But…
If you really want to enumerate all of the types that might get loaded in your AppDomain, you can approximate this by enumerating the types in the existing assemblies (i.e. as you've done here, with AppDomain.CurrentDomain.GetAssemblies(), but then for each assembly, call GetReferencedAssemblies()), which returns an array of AssemblyName values that you can use to load additional assemblies. For each of those, you can in turn inspect all of their types (to find the implementors of IUserControl) and to call GetReferencedAssemblies() to continue the recursive search.
Note that this still will not necessarily return all implementors of the IUserControl interface that your process might load. Assemblies can be loaded by means other than being referenced in your AppDomain's assemblies, such as by code searching a directory for candidates, or even the user explicitly naming an assembly to load. This is why using mechanisms directly supported by whatever API you're using is a much better approach, to make sure that you find exactly those assemblies that that API would find.
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.
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!
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
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);
When I first initialize an app domain it starts with 14 assemblies including mscor, System and others including System.Data
Is there a way for me to change define these starting assemblies?
Also in this new AppDomain I am compiling some code, the aim is to use this to limit the code that is compiled. If I add System.IO.File then it compiles fine, I want it to disallow this.
For the second part of the question:
I don't think you can control compilation at that level.
You could use a c# parser to disallow some namespaces but I'm pretty sure somebody will find a way to get throuh that protection (with reflection, etc)
The safest way is IMHO to control the execution and not the compilation, you can control execution with sandboxing: http://msdn.microsoft.com/en-us/library/bb763046.aspx
I have a program that utilises a plugin architecture. When the inital form loads, it scans the current directory, queries each dll and obtain some basic data which is displayed to the user.
Whilst using the program, the software will often need to ask the dll's to perform some work.
My question is, when the program initially checks the dll files, should I keep a reference to each dll object for future use, or should I query the dll files each time and create the object as and when needed?
if it's the first one, what is the best way to keep a list of an undetermined number of objects that derive from a common interface and then know which one to refer back to when needed?
Thanks.
Using the first one you could just create a
List<IYourCommonInterface> pluginDlls
and then just
pluginDlls.Add(dllReference);
Edit
Alternate method using a Dictionary, note that this will require you having some kind of ID for the dictionary that you can make use of to id the dlls.
Dictionary<SomeIDField, IYourCommonInterface> pluginDlls
pluginDlls.Add(dllRefrence);
Most apps do the check on load.
I wouldn't store the list of interfaces. If you do store them then you run into the possibility that the assemblies either disappear or are updated in some way. At which point you need to "refresh" them anyway.
Once you've loaded the assembly and gotten an instance of System.Reflection.Assembly for reflection (using Assembly.Load(), Assembly.LoadFrom(), Assembly.LoadFile(), etc.), the assembly is loaded. To quote MSDN on the subject:
...it is...possible to load specific assemblies into the current application domain
at runtime...There is no way to unload an individual assembly without unloading
all of the application domains that contain it. Even if the assembly goes out of
scope, the actual assembly file will remain loaded until all application domains
that contain it are unloaded. [emphasis mine]
So if you want to unload the DLLs until such time as you actually need them, you're going to have to create a new app domain and then unload that. Its easier to just load them and have done with it. You can keep the assemply reference around if you like, but if you call Assembly.Load() again, it won't actually load the assembly: it'll just get a reference to the previously loaded assembly.
If you've gone through the trouble of finding and loading the DLLs, normally you'll want to keep them around. This would depend mostly on how many resources the DLLs use and how your app is using the DLLs.
I'm assuming you're using LoadAssembly here. You can just store references to the assemblies you've loaded using some kind of map. Or even a list that you iterate through.
Perhaps if you give some more details we can help you better.
...and can those steps also be applied to a 3rd party assembly (that might already be strong-named)?
The context for my question should not be important, but I'll share anyway: I'm thinking of making a logger (or log-wrapper) that always knows what "log source" to target, regardless of whether the assemblies using it are in one appdomain, or spread across several appdomains. I think one way to achieve that, is to have a domain-neutral assembly with a static "LogSource" property. If that static property is set in a domain-neutral assembly, I think all appdomains will see it.
Assemblies aren't marked as domain-neutral in any specific way. You don't have to give them some specific attribute to make them domain-neutral. Any assembly can be loaded by the CLR either into the shared domain or the domain that triggered the assembly load depending on the configuration of the CLR instance that is loading the assembly.
How the CLR instance decides to load an assembly is dictated by policy. There are several ways to explicitly set this policy:
set LoaderOptimizationAttribute on your executable's entry point (usually Main). The CLR loader will apply the specified policy when starting the executable.
set the AppDomainSetup.LoaderOptimization property when creating a new app domain from managed code.
CorBindToRuntimeEx - when starting the CLR from unmanaged code, this function allows you to specify start-up flags, some of which control the loader optimization.
An assembly loaded as domain-neutral will be loaded into the shared domain. The app domain name is "EE Shared Assembly Repository" in CLRv4. That's not a real app domain, because it has no data and can't run any code. Assemblies loaded into it will share its code among all other running app domains. The byte code in the assembly will be JIT-compiled only once. All mutable data in the assembly, however, will be duplicated among the running domains. Static fields are not shared between app domains. Per-app domain static fields will be duplicated and different app domains will read and write in different places in the memory when referring to the same static field.
Aside: there is another kind of static fields - RVA statics, that are shared among all app domains in the current process. There is no way to declare such a field in C#, but it can be done in C++/CLI.
There is a trade-off in using domain-neutral assemblies. Access to static fields is slower. Since they're JIT-ted only once, but may access multiple instances of a per-app domain static field, any access to a static field goes through an additional indirection. When an assembly is loaded straight into the running domain, the address of the static field can be directly embedded into the JIT-ted code. However, when code compiled into the shared assembly tries to access a static field, it must first load the current domain's context and then find in it the static field address for this domain.
The decision whether to load an assembly into the shared domain or into the running domain depends on your use case, more specifically how many app domains you'd create and what sort of core you'd load into it.
If you load multiple domains that run essentially the same code, you'd want to share assemblies as much as possible, unless it's significantly hurting the performance of accessing static fields. An example is an application that decides to run portions of its own code in a separate app domain for the sake of isolation.
If you load multiple domains with different code, you'd want to share only assemblies that are likely commonly used by all the different assemblies. These would usually be the .NET Framework's own assemblies and all assemblies loaded from GAC. IIS works this way by default when running ASP.NET apps.
If you ever use only one app domain, you shouldn't share anything. A regular GUI application will be like that.
Note: mscorlib is always loaded into the shared domain.
Sources and further reading:
Application Domains and Assemblies
Domain Neutral Assemblies
Essential .NET, Volume 1, Addison Wesley; chapter 8 "AppDomains and Code Management"
Domain-neutral assemblies share only code across appdomains. However, data is still per-appdomain. Thus, there will be one copy of your static LogSource property for each domain.
Actually there is one tricky, undocumented, way to share data across domains, but it can cause runtime errors and whole application crash. Author don't recommend using it in real projects, so you can use MarshalByRef objects instead, to share log consumer.
But you can share it using that tricks too.
http://geekswithblogs.net/akraus1/archive/2012/07/25/150301.aspx