Unable to load assembly in new appDomain - asp.net

I have an mvc3 application.
I have a subfolder in my application project called "Plugins"
All of the dll's stored in that folder should be updateable during runtime, so then we an put down the appdomain and reload the new version of the dlls, so I am attemping to load all the plugin dll's in a different appDomain and set it's shadowCopy properties.
Looking through SO, msdn and some blogs, I got to this "solution". (this is called during my Application_Start)
static AppDomain pluginDomain;
static PluginHolder()
{
AppDomainSetup domainSetup = AppDomain.CurrentDomain.SetupInformation;
domainSetup.ApplicationName = "Plugins";
domainSetup.PrivateBinPathProbe = domainSetup.PrivateBinPath;
domainSetup.PrivateBinPath = GetPluginsDirectory();
domainSetup.ShadowCopyFiles = "true";
domainSetup.ShadowCopyDirectories = domainSetup.PrivateBinPath;
pluginDomain= AppDomain.CreateDomain("Plugins", null, domainSetup);
var item = pluginDomain.Load(File.ReadAllBytes(GetPluginsDirectory() + "Item.dll"));
}
"Item.dll" is the dll i am attemping to load. The last line throws a "Could not load file or assembly Item or one of its dependencies".
This seems to be the way other people have succeded with, but it just wont work for me.
I have no previous experience with AppDomains, so I am not sure on how to approach this problem, or if I am tackling it correctly.
Is my Assembly loading via the new AppDomain an ok approach?

To decypher these kind of error messages, you should use the Assembly Binding Log Viewer.
Also pay attention on the remarks section on the documentation page of AppDomain.Load(byte[]). If you want to load the assembly on the new AppDomain, then you should use CreateInstanceAndUnwrap instead.
If you want to implement a plugin engine in .NET, then you will need to understand AppDomains, assembly resolving and loading and a then choose on a mechanism for the communication between objects from different AppDomains. Most good books on .NET and CLR include a chapter or two on AppDomains and they will get you up to speed. Now as far as the cross-AppDomain communication is concerned, you could use WCF or .NET Remoting (MarshalByRefObject).
Good luck.

Related

UWP API in ASP.NET

I'd like to use a piece of Windows 10 specific UWP API (specifically, the Windows.Graphics.Printing3D stuff) in an ASP.NET code-behind DLL. Is there any way to do so?
While looking for a .NET-only resolution to this one, I've found a moderately clean way - a Win32/64 C++ DLL that would consume UWP API and present a COM- or P/Invoke-based interface to .NET.
Create a regular Win32 DLL. Build an interface for .NET to consume - exported functions or objects, depends. In my case, a single exported function will do. In the project's C/C++ settings, make the following changes:
Under General, set Consume Windows Runtime Extensions to Yes.
Under General, set Additional #using Directories to: C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcpackages;C:\Program Files (x86)\Windows Kits\10\UnionMetadata (assuming Visual Studio 2015)
Under Code Generation, set Enable Minimal Rebuild to No (it's only Yes for Debug, not for Release).
Then instantiate and use UWP components in the DLL in the usual C++/CX manner, like you would in a Store app, via using namespace Windows::... and ref new.
In this approach, you lose bitness agnosticism; an unmanaged DLL can't be "Any CPU". You win some, you lose some. Also, the site will not run without the Visual C++ redistributable package on the system. On the other hand, it may run faster than a .NET app; less managed/native boundary crossings.
Inspiration: "Using C++/CX in Desktop apps" by Pavel Y.
Open the project file as XML, and paste the following line under the first <PropertyGroup>:
<TargetPlatformVersion>10.0</TargetPlatformVersion>
Once you do that, the Add reference dialog will include UWP libraries, and the file type options in the "Browse..." dialog there will include .winmd.
Load the project, do Add reference/Browse, locate C:\Program Files (x86)\Windows Kits\10\UnionMetadata\Windows.winmd, add that.
There are some helpful extension methods in the managed assembly System.Runtime.WindowsRuntime (e. g. IBuffer.AsStream()), but for some reason, it's not listed under Assemblies. To reference it, you'd need to edit the project file directly, and under the first <ItemGroup>, add the following:
<Reference Include="System.Runtime.WindowsRuntime" />
Unlike the guide states, you don't need to change the compilation target to x86 or x64; leave AnyCPU be.
For desktop .NET applications, this is sufficient. For ASP.NET, however, there's a catch. The way the ASP.NET runtime sets up its AppDomains not compatible with UWP. It's probably a bug deep down, but I've reported it, and a Microsoft rep said the whole thing was not a supported scenario to begin with.
Anyway, you have to change the LoaderOptimization policy of the AppDomain to SingleDomain. The quickest way to do so is via abusing a private method of AppDomain:
AppDomain ad = AppDomain.CurrentDomain;
MethodInfo mi = ad.GetType().GetMethod("SetupLoaderOptimization", BindingFlags.Instance | BindingFlags.NonPublic);
mi.Invoke(ad, new object[] { LoaderOptimization.SingleDomain });
A good place to do that would be in the app startup code.
A slightly less dangerous approach would involve creating a new AppDomain, which would inherit all setup properties from the current one but LoaderOptimization, which will be set to SingleDomain, and running the UWP dependent code in that domain. Like this:
AppDomain CreateUnsharingDomain()
{
AppDomain cad = AppDomain.CurrentDomain;
AppDomainSetup cads = cad.SetupInformation;
return AppDomain.CreateDomain("Dummy", cad.Evidence,
new AppDomainSetup
{
ApplicationName = cads.ApplicationName,
ApplicationBase = cads.ApplicationBase,
DynamicBase = cads.DynamicBase,
CachePath = cads.CachePath,
PrivateBinPath = cads.PrivateBinPath,
ShadowCopyDirectories = cads.ShadowCopyDirectories,
ShadowCopyFiles = cads.ShadowCopyFiles,
ApplicationTrust = cads.ApplicationTrust,
LoaderOptimization = LoaderOptimization.SingleDomain
});
//Not sure which other properties to copy...
}
CreateUnsharingDomain().DoCallBack(MyUWPDependentMethod);
Again, it would make sense to create the domain once and cache it for the app lifetime.
This approach might be faster than the one with the monkey-patched AppDomain. The MultiDomain optimization exists for a reason; if you leave most of the Web code in a MultiDomain world, the optimization will do its work as intended.
Inspiration: "Walkthrough: Using WinRT libraries from a Windows Desktop application" by David Moore.

BuildManager.GetReferencedAssemblies equivalent for non-web applications

Compared to AppDomain.GetAssemblies(), BuildManager.GetReferencedAssemblies() (System.Web.Compilation.BuildManager) seems a more reliable way to get the assemblies that are referenced by an ASP.NET application at runtime, since AppDomain.GetAssemblies() only gets "the assemblies that have already been loaded into the execution context of this application domain".
Iterating through all assemblies is an important tool for dynamically registering types at application start-up in your DI container and especially during application start-up, chances are that other assemblies are not loaded (where not needed) yet, and the composition root is the first one that needs them. It is therefore very important to have a reliable method to get the application's referenced assemblies.
Although BuildManager.GetReferencedAssemblies() is a reliable method for ASP.NET applications, I'm wondering: what alternatives are available for other types of applications, such as desktop applications, windows services and self-hosted WCF services?
The only way I currently see is pre-fetching all referenced assemblies manually, just as the BuildManager does under the covers:
var assemblies =
from file in Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory)
where Path.GetExtension(file) == ".dll"
select Assembly.LoadFrom(file);
I've had the same problem. And after doing some research I've still not found a solid answer. The best I've come up with is to combine AppDomain.CurrentDomain.GetAssemblies() with the AppDomain.AssemblyLoad event.
In that way I can process all already loaded assemblies while getting notified for all new assemblies (which I then scan).
This solution is based on #steven's answer.
But would work in Web, WinForms, Consoles, and Windows Services.
var binDirectory = String.IsNullOrEmpty(AppDomain.CurrentDomain.RelativeSearchPath) ? AppDomain.CurrentDomain.BaseDirectory : AppDomain.CurrentDomain.RelativeSearchPath;
var assemblies = from file in Directory.GetFiles(binDirectory)
where Path.GetExtension(file) == ".dll"
select Assembly.LoadFrom(file);

Why do ASP.NET resolve assembly references differently?

I really tried hard to find a similar issue to get some leads, but no one seems to describe the case we are having, so here it goes.
Background
We have a product with the following general design:
[Local installation folder]
Contains a set of .NET assemblies, implementing the bulk of our product functionality.
Example: Implementation1.dll, Implementation2.dll
[GAC]
ClientAPI.dll. Our client assembly, to be referenced from end user Visual Studio projects. Has strong references to the implementation dll's in the local installation folder.
In ClientAPI.dll, we have an entrypoint we require end user projects to invoke. Lets call it Initialize().
The very first thing we do in Initialize is to install a so called assembly resolve handler on the current domain, using the AssemblyResolve event. This handler will know how to locate the implementation dll's and load them into the client process, using Assembly.Load().
Consider a console application. It will look something like:
class Class1
{
void Main(string[] args)
{
ClientAPI.Initialize();
// Use other API's in the assembly, possibly internally referencing the
// implementation classes, that now will be resolved by our assembly
// resolve handler.
}
}
Now, all is good in the console/windows forms/WPF world. Our assembly resolve handler is properly installed and invoked, and it can successfully resolve references to the implementation DLL's once ClientAPI.dll require their functionality.
Problem statement
With that being said, we intend not to support only console or WPF applications, so we were relying on the same design in ASP.NET. Hence, creating a new ASP.NET Web Application project in VS 2010, we figured everything would be as straightforward as:
class Globals : HttpApplication
{
void Application_Start(object sender, EventArgs e)
{
ClientAPI.Initialize();
// ...
}
}
A few 20-30 hours of dwelling in the ASP.NET runtime universe, trying the above in both the development server and in IIS, we've learned that things there are not really as we expected.
It turns out that in ASP.NET, as soon as the ClientAPI class is referenced anywhere, all references it has to any other assemblies are instantly resolved. And not only that: the results are cached (by design, since .NET 2.0 we've found), meaning we never have a chance at all trying to assist the CLR.
Without further elaboration about the different things we've tried and learned, it basically comes down to this question we have:
Why is ASP.NET resolving references like this? It is not compatible with how other types of applications does it, and even more, it is not according to the documentation of .NET / the CLR runtime, specifying that references to external types / assemblies are to be resolve when first needed (i.e when first used in code).
Any kind of insight/ideas would be highly appreciated!
Windows Forms / WPF applications run on individual client machines (and therefore run in a single, local context), whereas ASP.Net runs within IIS, within an application pool, on a server or set of servers (in a web farm situation). Whatever is loaded in to the application pool is available to the entire application (and therefore is shared between all clients who connect to the application).
HttpApplication.Application_Start is executed once, when the application starts up. It is not executed per client as it would be with a Winforms application - if you need to initialize something for every client that connects, use Session_Start or Session_OnStart, but then you may run in to memory issues with the server, depending on how many clients are going to connect to your web application. This also depends on whether your class is a singleton, and if the Initialize() method is static. If you have either of those situations, you're going to run in to cross-threading problems fairly quickly.
Additionally, it's worth noting that an idle IIS application pool will reset itself after a period of time. If no one uses the web application overnight, for example, IIS will flush the application's application pool and free up memory. These settings can be changed within IIS administration, but you should be careful when doing so - changing these settings to circumvent a badly designed object (or an object that isn't designed for a web application) can create even more problems.
FYI - I'm being a little fussy, but for the avoidance of doubt, the object is not cached - yes, it is loaded in to memory, but how the memory is managed is up to how you've designed the object (caching in the web world is an entirely different thing, and can be implemented in many different layers of your application).
Don't try and make a web application act like a windows application; you'll just create yourself more problems!

Get .NET assembly version for aspx file

I want to make a "properties style web form" that shows the application version for various .NET applications.
If I know the URL e.g. /someapp/default.aspx is it possible via reflection to execute that page and figure out the assembly version?
It's quite easy to find the executing assembly version, but without modifying the other application, is it possible?
Both the property page and the other application is running on the same server and in the same application pool.
Update: I've had some luck with
var url = "~/SomeApp/default.aspx";
var appType = System.Web.Compilation.BuildManager.GetCompiledType(url);
But navigating appType to find the assembly file version is not the same everytime.
Without modifying the web application to expose the version number through some URL-based retrieval (a simple page GET being the easy, obvious one), you're going to need to find a way to figure out where the DLL for the web application is from the URL.
If you can know the DLL's location, either by some convention (e.g. /appX/ is always at D:\Sites\appX\bin\appX.dll) or some configuration (you manually enter where each URL base's DLL is in a database), then you can retrieve that DLL's assembly version using the following code:
Assembly assembly = Assembly.LoadFrom("MyAssembly.dll");
Version ver = assembly.GetName().Version;
Code taken from this question.
Edit:
I've had a little look around, and there are some APIs to inspect the IIS configuration, so this is certainly a route to explore if you're trying to get from the URL to the assembly location. This question has an example of getting the physical path from the application/site name, for example. Microsoft.Web.Administration is the assembly to explore.
The ASP.NET engine streams nothing but HTML, javascript, etc.. to the client. There is nothing left of the assembly that gets passed in the response that can show what version of .net/asp.net that the application is running unless the developer on the server side adds it.
That said, you can gather some information from a utility at http://uptime.netcraft.com/up/graph that will give you some server information. Not down to the assembly version, but this is as close as I believe you are going to get.
You may implement custom HttpModule, put it to the bin folder of each application that you wish to monitor and append register this module in web.config files. In this module for example you should handle request, retrieve all required information and put it to response cookie.

Problem reflecting in ASP.net context

I have a ASP.net application that is referencing a external assembly that I need to dynamically load and discover any types implementing a known interface. The problem I am having is that the type I reflect does not match the same interface that is running and so I cannot cast it.
Example:
This code is run in ASP.net app.
var assembly = Assembly.LoadFile(Path.Combine(HttpRuntime.BinDirectory, "ExternalAssembly.dll"));
var type = assembly.GetExportedTypes().First<Type>(x => x.Name == "AClass"); // AClass implements IAInterface
var reflectedInterface = type.GetInterface(typeof(IAmAInterface).ToString());
if (reflectedInterface != typeof(IAmAInterface))
throw new Exception("This makes me sad"); // This code gets run
The only difference I can see between the reflected interface I loaded from the bin and the interface returned from typeof is that the typeof assembly has a location in the temp ASP.net path (C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\root\08c43c8b\3adac8cf\assembly\dl3\eb7a4127\0235ea60_a3c8c901\ReflectionTest.DLL)
Thanks Paul Alexander I have changed the code to use the Assembly.Load method not Assembly.LoadFile which solves the problem.
wwilden: I also tried extracting the interface into it's own assembly and this does also solve the problem.
When you use LoadFile the assembly is not loaded into the same context as your other assemblies at runtime so to the CLR interface runtime types are different. You can read more in Suzanne Cook's Debugging Assembly Load Failures.
If the assembly that you're loading is already in the Bin directory - you can load it by name. You don't need to know the exact path as the Bin folder is already in the assembly probing path.
Where is your interface type defined? Does it exist both in the reflected assembly as well as in the application itself? Then you actually have two different interfaces, even though they have the same namespace and name.
What you need to do is extract the interface from the reflected assembly and put it into another assembly that you refer to both from the reflected assembly as well as your application. Then it should work.
Apart from your problem, if you have a lot of assemblies to dynamically load, remember that they will remain in memory until the ASP.NET worker process reloads. This could influence your server's performance.
You could load the assemblies in a separate AppDomain (the smallest unit that is possible to unload), load a proxy class in that AppDomain which you reference with .NET remoting. Once finished, you unload the AppDomain.
There is an interesting article over at CodeProject about a situation like this. Where you have a class that has a structure that is identical with an interface without implementing the interface itself. The article outlines a method of dynamically creating wrapper classes that implement the needed interface. It could be helpful in your situation.

Resources