views:

120

answers:

2

We are using CSLA (a rather ancient version) in our .NET 3.5 applications, and we make use of it's NetRun application loading for some of our users. For those not familiar with NetRun, NetRun.exe is basically an application "runner" that is installed on user computers (e.g. to c:\Program Files\NetRun\NetRun.exe). The user simply starts the application by launching NetRun.exe.

What NetRun.exe does is the following:

(1) Creates a new AppDomainSetup

Dim setupDomain As New AppDomainSetup()
setupDomain.ApplicationBase = CurrentDomainPath() ' this will be C:\Program Files\NetRun\
setupDomain.ConfigurationFile = "http://www.ourdomain.com/TheApp.xml" ' The app.config file is actually named TheApp.xml on the server because it has NetRun-specific config settings that don't belong in the standard TheApp.config that is used when the app is running directly from the server.

(2) Then a new AppDomain is created with that AppDomainSetup

' create new application domain 
Dim newDomain As AppDomain = AppDomain.CreateDomain("TheApp", Nothing, setupDomain)

(3) Then NetRun.Launcher (a launch helper class -- mainly used for a common splash screen) is instantiated within that new AppDomain via:

' create launcher object in new appdomain
Dim launcher As Launcher = CType(newDomain.CreateInstanceAndUnwrap("NetRun", "NetRun.Launcher"), Launcher)

(4) Then the luncher helper class runs the app within the new AppDomain via

' use launcher object from the new domain to launch the remote app in that appdomain
launcher.RunApp()

(5) Which, after all the splash-screen stuff, the application is finally launched via

Dim ass = Assembly.LoadFrom("http://www.ourdomain.com/TheApp.exe")
ass.EntryPoint.Invoke(ass.EntryPoint, Nothing)

So, to recap, the actual running application's AppDomain's ApplicationBase is C:\Program Files\NetRun\, while the EntryPoint to the actual app is found in "http://www.ourdomain.com/TheApp.exe". So far, so good.

The application itself, in addition to TheApp.exe, also as a dependency on ALibrary.dll. To date, this has worked wonderful for us. Even when ALibrary.dll has config entries that were pulled into TheApp.xml file, those config entries have always been read fine (and continue to do so).

With our upcoming release, we added a new custom config file section, which is defined in ALibrary.dll. We added the new section into TheApp.xml and TheApp.config files

<configSections>
    <sectionGroup name="applicationSettings" type="System.Configuration.Applicat...">
      <section name="TheApp.My.MySettings" type="System.Configuration.ClientSettingsS..."/>
      <section name="ALibrary.My.MySettings" type="System.Configuration.ClientSettingsS..."/>
    </sectionGroup>
    <!-- NEW CONFIG SECTION -->
    <section name="fixedPriceExceptionModel" type="ALibrary.Configuration.FixedPrices.FixedPriceExceptionModelSection, ALibrary"/>
</configSections>

(parts were ...'ed out to conserve space in this post)

However, when we try to access the new section in a NetRun launched TheApp.exe in the "normal" way via

CType(ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None) _
                             .GetSection("fixedPriceExceptionModel"), FixedPriceExceptionModelSection)

We get the following exception:

An error occurred creating the configuration section handler for fixedPriceExceptionModel: Could not load file or assembly 'ALibrary' or one of its dependencies. The system cannot find the file specified. (http://www.ourdomain.com/TheApp.xml line 8)

Doing some research, this is due to the fact that the assembly resolver for the .NET configuration libraries are looking for ALibrary.dll in all the usual places; ApplicationBase, specific sub directories, .NET core directories, and then the GAC. Unfortunatly, ALibrary.dll is never going to be found in any of those locations. When this application is run directly on the server, without NetRun, the application does not throw the exception and properly reads the configuration section.

One thing I tried was to set the AppDomainSetup differently, in that its ApplicationBase would be set to http://www.ourdomain.com/, but then the subsequent call to CType(newDomain.CreateInstanceAndUnwrap("NetRun", "NetRun.Launcher"), Launcher) bombs because NetRun.exe is not on the server.

This is probably a lot to take in and I hope I described it well enough (and didn't bore you). Maybe there is no easy solution, but I've exhausted my limited knowledge of this area and hope someone else here on SO might have the magic fix, allowing me to reliably access custom config sections while running the application in both NetRun and local.

A: 

As far as I understood the problem, the thing is that code is run on client machine without real (phisical) presence of the assemblies, as they are loaded from the server.

Knowing it, you have two options:
1. Die hard trying to find a solution with existing code ;)
2. Not use a custom configuration section handler.

Personally, I'd go with #2.

Instead of using ALibrary.Configuration.FixedPrices.FixedPriceExceptionModelSection as your config section handler (which causes exception, as this type cannot be found on the client), use the default System.Configuration.ClientSettingsSection as other sections do - this is part of the .NET Framework, so no problem here on the client. After that, create a factory for the needed configuration class (if you really need that strongly typed), that would read configuration step-by-step and create your FixedPriceExceptionModel object, filled with data and available to use by anything needing it.

If I misunderstood the problem, let me know.

mYsZa
No, you didn't at all misunderstand the problem - spot on. Yeah, even if I set up ShadowCopy and physically pull down the files, it doesn't help due to the way that ShadowCopy spreads the assemblies files out.Your #2 is an obvious solution, but I'm going to keep holding out for someone to coming up with that magic bullet to make your #1 option true :) It's a real bummer to see this fail like it does :( Thanks for your response.
ckittel
I ended up going with #2 :( Just took my config XML and plopped it into its own file and loaded/parsed it myself.
ckittel
A: 

I don't think this will ever work as you've presented it.

This line of code works, because LoadFrom is a magic method that looks in a specific location for the assembly and any of its dependencies. TheApp.exe and its DLLs get loaded from this magic LoadFrom context:

Dim ass = Assembly.LoadFrom("http://www.ourdomain.com/TheApp.exe")

However, the config section assembly load fails, because the .NET framework code that loads it is (presumably) using a plain Assembly.Load call, which is disconnected form the original LoadFrom call and has no knowledge of www.ourdomain.com. There are various ways of getting additional directories into the .NET assembly search path, but these are all taken relative to the app domain base directory.

As you've pointed out, the only other way to have Assembly.Load find out about www.ourdomain.com is to alter the app domain base directory. In which case, is it so bad to deploy NetRun.exe onto the web server?

Tim Robinson