A few weeks ago, I wrote about the problems that FakeItEasy’s assembly scanning was causing while it was looking for user-defined extensions. To recap, FakeItEasy was scanning all assemblies in the AppDomain and the working directory, looking for types that implemented
IFakeConfigurator. This process was quite slow. Worse, it raised LoaderLock exceptions when debugging, and Runtime errors anytime I ran my tests using the ReSharper test runner.
At that time, I’d opened issue 130, intended to allow configuration of the scanning procedure. I’m happy to say that the issue has been closed “no fix”. Instead, I’ve contributed the fix for Issue 133 — Improved performance of assembly scanning. It doesn’t introduce any configuration options, but streamlines the scanning process.
The original behaviour was:
- find all the DLLs in the application directory
- load all the found DLLs
- find the distinct assemblies among those loaded from the directory and those already in the AppDomain
- scan each assembly and add all the types to a list
The new behaviour, heavily inspired by Nancy‘s bootstrapper-finding code, is:
- find all the DLLs in the application directory
- discard DLLs that are already part of the AppDomain – We don’t even have to crack these files open again, since we already know everything about them. Note that this check examines the absolute paths to the DLL and the loaded assembly, and will be fooled by shadow copying. So, if your test runner makes shadow copies, this time won’t be saved. I turned off shadow copying with no ill effects (and a tremendous speedup), but your mileage may vary.
- load each remaining DLL for reflection only – This may be faster, and it may not, but it has another big advantage – it doesn’t cause any of the code in the assembly to execute. (It was the execution of the assembly code that caused my LoaderLock and Runtime errors.)
- for each assembly that references FakeItEasy, fully load it – If we don’t do this, we can’t scan for all the types in the assembly because
When using the ReflectionOnly APIs, dependent assemblies must be pre-loaded or loaded on demand through the ReflectionOnlyAssemblyResolve event.
according to the error I got when I tried it. Note that excluding assemblies that don’t reference FakeItEasy means we only examine assemblies that could possibly define formatting/dummy/configuration extensions, cutting down on the scanning time.
- scan each of the following, remembering all contained types:
- the assemblies we just loaded from files,
- the AppDomain assemblies that reference FakeItEasy, and
- FakeItEasy – We need to include FakeItEasy explicitly because it defines its own formatter extensions, and since we’re otherwise only looking at assemblies that reference FakeItEasy, we’d miss it.
This new scanning behaviour has been released in the FakeItEasy 1.13.0 build, and has been a boon to me already. I’m enjoying the faster test runs (0.534 seconds for my first test, versus 1.822 (or more)) and the improved stability of the test runner. NuGet it now.
Late last week at the Day Job, a colleague came to me with a problem. The web service he was trying to hit was throwing an error he’d never seen before:
HTTP Error 500.19 – Internal Server Error
The requested page cannot be accessed because the related configuration data for the page is invalid.
I’d never seen it before either, at least not in this exact incarnation. Take a look
In case the text isn’t so clear, here are the details:
|Logon Method||Not yet determined|
|Logon User||Not yet determined|
The errors suggested that we have problems with the configuration file, but the web.config was present (and well-formed), and there were no obvious permission problems, so it seems the file was being read. There was nothing in the event logs. Web searches yielded nothing that matched the
0x80072af9 error code or the description of the error. Even ERR.exe, recommended by Troubleshooting HTTP 500.19 Errors in IIS 7, failed me.
Fortunately, there were sibling virtual directories on the server, and they were working fine, even under the same App Pool. I knew that this virtual directory, unlike the others, restricted access to a whitelist of IP addresses. So, I changed the
true, just in case for some reason the clients’ IP addresses weren’t being detected properly. No change.
Frustrated, I removed the whole
security node. The service worked!
So I took a closer look at the node:
<security> <ipSecurity allowUnlisted="false"> <add ipAddress="127.0.0.1" allowed="true" /> <add ipAddress="184.108.40.206 " allowed="true" /> </ipSecurity> </security>
Check out that “220.127.116.11” ipAddress. Now check it again. It’s actually “18.104.22.168 “, with a space at the end. (I bolded the space there, just so you wouldn’t miss it.) It seems that this messes up the IP parsing, and IIS is completely flummoxed. Remove the space, and all is well.