Why do ASP.NET resolve assembly references differently? - asp.net

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!

Related

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);

When does the CLR try to load a referenced assembly?

I want to write a small installer app that installs a web site and creates IIS virtual directories. The app should run on Windows XP/Server 2003 (IIS 6) as well as on Vista/2008 (IIS 7).
The problem is: for IIS 6 we create virt dirs by calling WMI/Metabase API, for IIS 7 there is a much better API: Microsoft.Web.Administration, but its assembly is available only on IIS 7 systems.
Naive approach:
...
if (OperatingSystem == old)
{
call metabase API...
}
else
{
call Microsoft.Web.Administration...
}
...
Nice, isn't it? But how can I make sure that this does not crash on a old system just while trying to load the Microsoft.Web.Administration DLL? Or is an assembly just loaded, when it is first used? When a method that is calling into the assembly is first used?
I guess, testing does not help without some determinism being guaranteed by CLR/.NET spec.
I am really looking forward to hearing your experiences, hints or solutions for this topic. I have not found anything remotely usable on the web so far.
I have not been able to find the definitive answer as in a specification stating when assemblies must and must not be loaded. However, according to
http://msdn.microsoft.com/en-us/magazine/cc163655.aspx (section "Load Fewer Modules at Startup")
and the book extract at www.informit.com/articles/article.aspx?p=30601&seqNum=5 (extract from "Essential .NET, Volume I: The Common Language Runtime").
the JIT of the CLR will load a needed assembly only when needed to compile a method. Thus, you should move any use of Microsoft.Web.Administration... to a seperate method which is only called when your confident that the assembly exists on the system. That is,
setup()
{
if ( Operating.System == Old )
call metabase API
else
doIIS7Setup()
}
void doIIS7Setup()
{
call Microsoft.Web.Administration ....
}
Personally, rather than trying to rely on any inbuilt behaviour of the JIT, I'd move the dependency on Microsoft.Web.Administration to another assembly altogether.
Then, somewhere in the calling assembly, I'd check to see if %systemroot%\inetsrv\Microsoft.Web.Administration.dll is present. If so, then I'd assume I'm using the managed interface and call the assembly; if not, I'd revert to the metabase API.

Using web services in different environments

We have a series of web services that live in different environments (dev/qa/staging/production) that are accessed from a web application, a web site, and other services. There are a few different service areas as well. So for production, we have services on four different boxes.
We conquered the db connection string issue by checking the hostname in global.asax and setting some application wide settings based on that hostname. There is a config.xml that is in source control that list the various hostnames and what settings they should get.
However, we haven't found an elegant solution for web services. What we have done so far is add references to all the environments to the projects and add several using statements to the files that use the services. When we checkout the project, we uncomment the appropriate using statement for the environment we're in.
It looks something like this:
// Development
// using com.tracking-services.dev
// using com.upload-services.dev
// QA
// using com.tracking-services.qa
// using com.upload-services.qa
// Production
// using com.tracking-services.www
// using com.upload-services.www
Obviously as we use web services more and more this technique will get more and more burdensome.
I have considered putting the namespaces into web.config.dev, web.config.qa, etc and swapping them out on application start in global.asax. I don't think that will work because by the time global.asax is run the compilation is already done and the web.config changes won't have much effect.
Since the "best practices" include using web services for data access, I'm hoping this is not a unique problem and someone has already come up with a solution.
Or are we going about this whole thing wrong?
Edit:
These are asmx web services. There is no url referenced in the web.config that I can find.
Make one reference and use configuration to switch the target urls as appropriate. No reason to have separate proxies at all.

ASP.NET - context-agile object,Data sharing concept in application domain?

I read some articles about Application Domain.The deep reading finally resulted in whirling
confusion.So I submit the questions to subject experts.
1) As CLR takes care of creating AppDomain as and when needed,could there be a critical
need to go for manual Application Domain Creation ?
2)I heard that one application domain can not share data with other application domain (i
am not sure).What about the case of windows communication foundation ?
3) Normally the basic libraries (system.dll,mscorlib.dll) are loaded in default application domain. can i load them in custom created application domain ? if it is possible,will CLR keep a copy in Default application domain ?
like
------------------ ----------------
Default AppDomain Custom Appdomain
------------------- ----------------
mscorlib.dll mscorlib.dll
System.dll System.dll
..... .......
----------------- -----------------
4) What is the term context-agile object in application domain referes to?
Creating your own AppDomains is useful sometimes when you want isolation (e.g. sandboxing 3rd party code), or the ability to reload code which changes during execution. (You can't unload an assembly, but you can unload an AppDomain.)
Sharing data between AppDomains involves marshalling. The data can either be marshalled by value (i.e. everything gets copied) or by reference if your object derives from MarshalByRefObject. In the latter case, what actually gets through to the other AppDomain is a reference to a proxy object. Anything you do on the proxy is actually done to the real object in the original AppDomain.
Not entirely sure what you mean. You can certainly use all the system assemblies in other AppDomains.
I haven't come across this term, that I can remember.
AppDomains can pass information from one to the other using services, such as WCF as you said in question 2.

Deploying Flex Projects Leveraging Imported Web Services

I'm sure there's a simple explanation for this, but I haven't had much luck at finding the answer yet, so I figured I'd put the word out to my colleagues, as I'm sure some of you've run into this one before.
In my (simple) dev environment, I'm working with a handful of WCF Web Services, imported into my FB3 project and targeting a local instance of the ASP.NET development Web server. All good, no problems -- but what I'd like to know now is, What's the right way to deploy this project to test, staging and production environments? If my imported proxies all point, say, to http://localhost:1234/service.svc (from which their WSDLs were imported), and all I'm deploying is a compiled SWF, does Flex Builder expect me to "Manage Web Services > Delete", "> Add", recompile and release ever time I want to move my compiled Flex project from development to test, and to staging, and ultimately into production? Is there a simpler workflow for this?
Thanks in advance -- hope my question was clear.
Cheers,
Chris
If you have path names which will change depending on the enviroment then you will likely need to recompile for each environment since these will be compiled in the swf.
I typically use ANT scripts to handle my compile/deployment process when moving from development and production environments. This gives me the ability to dynamically change any path names during the compile. These build files can be integrated into Flex Builder making this process very easy once you have everything set up, and can be done with one click or scheduled.
Thanks Brett. I've been meaning to dig into automating my build processes anyway, so now's probably as good a time as any. :)
You do not need to build a SWF for each environment. Here's a technique I use commonly:
Externalize your configuration properties into an XML file; in this case, it could be a URL for each service or a base URL used by all your services
When the application starts up, make an HTTPService call to load the XML file, parse it, and store your properties onto some bindable "configuration object"
Bind the values from that object against your objects that depend on the URLs
Dispatch an event that indicates your configuration is complete. If you have some kind of singleton event dispatcher used by some components in your app, use that, so that the notification is global
Now proceed with the rest of the initialization of your application
It takes a little work to orchestrate your app such that certain parts won't initialize until steps 1-5 take place. However I think it's good practice to handle a lot of this initialization explicitly rather than in constructors or various initialize or creationComplete events for components. You may need to reinitialize things when a user logs out and a different user logs in; if you already have your app set up to that initialization is something you can control then reinitialization will not be a problem.

Categories

Resources