Why do ASP.NET resolve assembly references differently?
Asked Answered
K

1

6

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!

Kutzer answered 11/8, 2011 at 12:13 Comment(2)
great question. needs some bump.Thibaud
I've recently ran into this problem as well. It is very frustrating and there is no real good solution. One solution now exists though in ASP.NET 4 in the form of the PreApplicationStartMethodAttribute, which allows you to run code very early in the ASP.NET pipeline thus allowing you to handle resolving your assemblies. The WebActivator project available on NuGet makes use of this new attribute as well as extends it. Of course this only works for ASP.NET 4... More information can be found here: haacked.com/archive/2010/05/16/…Bialystok
P
0

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!

Pskov answered 10/2, 2012 at 13:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.