Configuration Type Selector

Topics: Enterprise Library Core, Exception Handling Application Block
Apr 16, 2007 at 10:47 PM
Edited Apr 16, 2007 at 11:07 PM
It is so convoluted I don't even know where to start.

RE: Sample Application ExceptionHandlingWithLogginQuickStart

I can't edit app.config from with in VS2005 using the GUI I get unable to load assembly...... and the configuration does not start. So I run the external one.

If I run the EntLibConfig from the source directory C:\EntLib3Src\App Blocks\bin, I can load the application config file. I then drill down to the custom error handler and walk through the steps to bring up the Type Selector to change/view the custom Exception Handler. I then push the "Load an Assembly..." button find the .exe and load the assembly. I see the Custom Error handler.

BUT If I run EntLibConfig from the start menu and do the same thing I get:

"There were no types found in the assembly 'ExceptionHandlingWithLogginQuickStart' that implement or inherit from the base type '......ExceptionHandling.IExceptionHandler'.

If I load the Enterprise library source into VS2005. Choose the console as the start up and run the EntLibConfig in debug mode. I get the above error. But when walking through the code this is where I'm puzzled. I have not been able to get a debug session to return back a possitive so during this line of code I don't know what the expected results should be and how they are derived. I get the concept is the object derived from this object. Looking at the code I would say true: public class AppMessageExceptionHandler : IExceptionHandler

The test in the code then should return TRUE but return FALSE is this:
TypeSelector.cs line 162

valid = baseType_.IsAssignableFrom(selectedType);

I don't know if it is that or if issue is during the loading of the assembly and if there is a proxy in place instead of the acctual object?

How I came accross this was when I was trying to implement my own using GAX. After filling in the TODO's and stuff then compiled a simple Assembly with just the code produced from GAX and the modified TODO's. After that I built a Unit Test logic for the harness and for that application I went to Ent Lib Config to use the custom error handler only to get the error above.

I knew this was flaky in 2.0, but is it any better in 3.0? Like not have referenced assemblies in the assemly you want to load the type from? If this is still an issue will this be address as a patch or something? It seams like very little has change and no improvements in this area from 2.0. I know plug-ins, and loading external assemblies is a pain. With Orcas or something else MS is to come up with a plugin/addon name space to help with reflections and loading external assemblies (CLR Inside Out Feb/March 2007). Will this be address then if not now?


Apr 17, 2007 at 5:10 AM
Sorry about the confusion. The issue here is that in EntLib v3 you essentially get two copies of EntLib - one signed (precompiled binaries) and one unsigned (that you compile from the source). Despite the fact that the code is identical in the two versions, they are completely incompatible with one another as far as the type system is concerned, since the public keys differ. If you use the config tool from the strong-named version then the tool will write the public key to the files and will look for the strong-named version of any assemblies it cares about (such as when using the type selector). However if you use the copy of the tool you compile from the source code, it will expect non-strong-named assemblies. The errors you are seeing are likely due to a mismatch between the tool and the extension assemblies or config files with the wrong public key.

BTW when using the Visual Studio-integrated config editor, you also get the choice to use strong-named or non-strong-named assemblies. By default the tool will use the strong-named versions, but on each solution you can point it to the unsigned (or any other set of) assemblies by setting the EnterpriseLibraryConfigurationSet property after selecting the root selection node.

FWIW we did try hard to make the config tool less flaky in v3 by ensuring it didn't need to load the assemblies for any custom providers every time you open a config file.

Hope this helps
Tom
Apr 17, 2007 at 6:49 AM
Edited Apr 17, 2007 at 6:55 AM
Yes, that is where I thought this might be going. But is that with trying to load an assembly in the Type Selector or trying to get the EntLibConfig to work in VS 2005 with or with out strong-named assemblies.

I'm not working with signed assembly, and correct me if I'm wrong but the Source Code when compiled is not signed. So when debugging there are no signed, and also when debugging I can get it to work with some but not others.

I'm debugging EntLibConfig from VS2005 in C:\EntLib3Src\ (Configuration.Console)

I startup a new app config add Exception Handling, add a policy, a custom handler. I load my Custom Exception Handler assembly and get the
"There were no types found in the assembly 'Your assembly here' that implement or inherit from the base type '......ExceptionHandling.IExceptionHandler'.

I load one from Sample Application ExceptionHandlingWithLogginQuickStart and depending on where or how I load it (debugging, start menu, integrated w/vs2005) I may or may not get the error. That is why I thought it might be with strong names.

Regardless the environment, if it does not work I modified with this:

valid = baseType_.IsAssignableFrom(selectedType);

if (!valid)
{
Type foundInterface = selectedType.GetInterface(baseType_.FullName, true);
if(foundInterface != null)
valid = true;
}

Why would that work but not the other?

And that works but the same issue then happens few line later, where it can't identify interface, attributes, or derived clases:

object[] configurationElementTypeAttributes = selectedType.GetCustomAttributes(typeof(ConfigurationElementTypeAttribute), true);

The attribute is there but through refactoring it is not, and now on that one I stumped. Other than commenting it just to get it to work???

When I look at the assembly in ILDASM I see everything there that is required?, interface and the attribute(ConfigurationElementTypeAttribute, IExceptionHandler).

implements Microsoft.Practices.EnterpriseLibrary.ExceptionHandlingMicrosoft.Practices.EnterpriseLibrary.ExceptionHandling.IExceptionHandler

.custom instance void Microsoft.Practices.EnterpriseLibrary.CommonMicrosoft.Practices.EnterpriseLibrary.Common.Configuration.ConfigurationElementTypeAttribute::.ctor(class mscorlibSystem.Type) = (

Also regardless of how to fix it, is there a better way to report the error? Becuase of the incorrect or vague error message sends one on a wild goose chase trying to figure out why the error is telling them they did not Implement IExceptionHandler. When cleary one has done so Your X does not equal X. Is there more pre qualification and/or refactoring that can be done?

Also, is there a way to do this validation for signed and if the user is using strong-names then validate if the loaded assebly meats the requirerments to be loaded in that version of EntLibConfig. Instead of the user trying to figure out if they are working with signed or not.

I'll continue, is it strong-naming? is it multiple EntLib assemblies being loaded when they are already loaded one from EntLibConfig and one when loading the exception handler during the Type Slector dialog box? Is there some proxy object being built that does not have the real implmentation of the object?



P.S. It is as if there are too many possible issues (flaky), so just assume they did not implement something and then show an error message to that effecct. How many possibilities could there be?
Apr 17, 2007 at 5:44 PM
Edited Apr 17, 2007 at 5:45 PM
Yes, something with working on the signed versions of EntLibConfig and if you run from within VS 2005. Aslo if you reference sigend or none signed. I think EntLibConfig loads one type signed/none-signed then when loading my assemblie with reference entlib it was the opposite. I have yet to figure out the matrix as to what works, what does not, how it works, and what combinations. Is it EntLibConfig standa alone? Integrated? Signed? None-Signed? Do you reference signed or none signed? Is your assemblie signd on not signed?

1. Make sure the references point to C:\EntLib3Src\ (none-signed)
2. Make sure your application is not signed.
3. Compile
4. Run EntLibConfig with in VS 2005 add exception handling, add policy, add custom handler. No Worky Vague error message. running from c:\program files (signed)
5. Run EntLibConfig from c:\EntLib32src works. (none-signed)

Then I signed the project
1. Create the key and sign the application
2. Make references to the signed version c:\program files
3. Build
4. Run EntLibConfig with in VS 2005 add exception handling, add policy, add custom handler. Works.
5. Run EntLibConfig from c:\EntLib32src. No Worky

This test prooves your point and builds a basis for testing. Now what I need to do is create two source environments one signed and one none-signed. Do some debugging and mix and match.

But, again why such a bogus error message when cleary the issus is not becuase I did not implement something or add an attribute when cleary they are there? And if there is a know combination issues with signed and none-signed why not show a particular error message for that along with the option that you pointed out EnterpriseLibraryConfigurationSet?

Why is there not a better intergation of what is going on with better error reporting? Am I the only one running into issues with Type Selector that where so obvioius in version 2.0 and continues over into 3.0? The same type of things existed in 2.0 where too many posabilities so just report that the interface, class, and/or attribute was not implemented.

I'll be working on this and let you all know if I find anything. I will find which combination don't work, how to detect them and give warnin/error accordingly. To the effects if you are using signed EntLibConfig w/out signed assemblies, etc...

Apr 17, 2007 at 6:51 PM
Here is a description of the assemblies and config tools you should use for various scenarios, assuming you install everything to the default location:

Scenario Reference assemblies from Standalone Tool EnterpriseLibraryConfigurationSet for VS editor
Microsoft signed binaries ...\Microsoft Enterprise Library 3.0 - April 2007\bin ...\Microsoft Enterprise Library 3.0 - April 2007\bin\EntLibConfig.exe (Machine Default) or Microsoft Signed
Unsigned binaries ...\EntLib3Src\bin ...\EntLib3Src\bin\EntLibConfig.exe EntLib3Src
Your own signed binaries (path where you have your signed binaries) (EntLibConfig.exe built with your signed binaries) (Configuration Set that you define in the registry, or EntLib3Src if you did not change the path before strong-naming)

HTH
Tom
Apr 17, 2007 at 7:17 PM
Got it, but all that one needs do is build an application to extend the application block by adding a handler or a formatter. All they want to do is install the binaries only c:\program files. Then all they would need to do is build an Exception Handler (they could even use GAX). If there project is not sigend then all hell breaks loose when tyring to configure an app.config to use that Handler. Because if they either run stand alone outside of VS2005 "c:\program files\" or with in VS 2005 they will get the error (using signed EntLibConfig with a none-signed Handler and/or formatter).

All they would need to do is sign thier application and they would be good to go. But what they get is some error message not even relating to a signing issue but one relating to not implementing the interface. Why because the error detection is flaky at best. This only then sends the programmer on a wild goose chase trying to figure out what the requirements are to implement the Handler. The possiblity of logicaly figuring out the signing matrix is there and nothing is done to help the programmer in the right direction.

Here is some code to test for signed assmblies and to see if there is a mismatch (TypeSelectorUI.cs after line 293

 
Assembly entLibAssembly = Assembly.GetEntryAssembly();
byte[] entLibPublicKeyToken = new AssemblyName(entLibAssembly.FullName).GetPublicKeyToken();
bool entLibSigned = entLibPublicKeyToken != null && entLibPublicKeyToken.Length != 0;
 
byte[] assemblyPublicKeyToken =
    AssemblyName.GetAssemblyName(originalAssemblyFileName).GetPublicKeyToken();
bool assemblySigned = assemblyPublicKeyToken != null && assemblyPublicKeyToken.Length != 0;
 
if (entLibSigned && !assemblySigned)
{
    DisplayMessageBox(
        "Attempting to load a None-Signed assembly into a Signed version of EntLibConfig.",
        "Signed Assemblies Missmatch");
    return;
}
 
if (!entLibSigned && assemblySigned)
{
    DisplayMessageBox("Attempting to load a Signed assembly into a None-Signed EntLibConfig.",
                      "Signed Assemblies Missmatch");
    return;
}
 

Apr 17, 2007 at 7:59 PM
Fair call, we really should have anticipated that this would be a common cause of confusion and provided more detailed error messages as you have done. Thanks for providing the snippet.

BTW even when using a strong-named version of EntLib, it should not be necessary to strong-name any extensions or custom providers. In general if assembly A references assembly B, then either both must be signed, both must be unsigned, or only B can be signed. This last option is the case where an unsigned block extension references the signed EntLib binaries.

Tom
Apr 17, 2007 at 8:20 PM
Thank You, I will continue my testing knowing that sandboxing/addons/plugins/refractoring is pain. I have the same issues with application I build and knowing how others do it/or don't helps me as well as them. This is why when I got EntLib 3.0 I went right to the Type Selector to find improvements if any. I'll be continuing to build a simple guidence/template for an application where low level errors are being logged, but also show the error being rethrown only to be trapped in the above layer. This layer being the application layer would use a custom handler to show a dialog box and interact with the user. Very simple stuff but shows how to piece together a low level policy, trapp an error and move it up to a higher level policy. This demonstrates how the two policies handle the same error but differenlty based on which layer is handling the error. One policy (Data Policy) writes an event entry with all the guts of a stack trace, while the other policy (Application Policy) would show a user friendly dialog box. This is a typical situation one would run into and the most typical reason for writing a custom handler and how to propery use the rethrowing of the error and how it bubles up to the surface.

Apr 18, 2007 at 7:24 PM
Edited Apr 18, 2007 at 7:30 PM
I'm almost done with a dual source environment one Signed and one None-Signed. Another snippet of code I added was in the About Box. In case I'm communicating with a programmer about what version she/he has because I may not be involved with the install, but may need to support after an install. I need a way to know if the EntLibConfig they are using is Signed or not and if helpfull show the Public Token. With this one can tell if it is signed and where it came from either inhouse or ms or some hack base on the public token. There is a crude hex converter included that bloats the code a bit, but the lookup table is there for performance instead of doing a ToString on every byte. These are the required changes to code and the form.

The change on the AboutForm.cs is add a template {signed} in the linkLabel1.text:

Microsoft patterns && practices Enterprise Library{newline}{newline}Configuration 
Tool version {version}{signed}{newline}{newline}http://msdn.microsoft.com/practices
{newline}{newline}{newline}Copyright © 2007 Microsoft Corporation. All rights 
reserved.{newline}{newline}Contains code adapted from ACA.NET with permission from 
Avanade Inc.{newline}{newline}ACA.NET copyright © Avanade Inc. All rights reserved.

Then add some code behind to replace the {signed} with the a message if it is signed and the PublicKeyToken.

First I made a field for the Hex Lookup Table:

static string[] HEXVALUES = { "00", "01", "02", "03", "04", "05", "06", 
    "07", "08", "09", "0A", "0B", "0C", "0D", "0E", "0F", "10", "11", "12", 
    "13", "14", "15", "16", "17", "18", "19", "1A", "1B", "1C", "1D", "1E", 
    "1F", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2A", 
    "2B", "2C", "2D", "2E", "2F", "30", "31", "32", "33", "34", "35", "36", 
    "37", "38", "39", "3A", "3B", "3C", "3D", "3E", "3F", "40", "41", "42", 
    "43", "44", "45", "46", "47", "48", "49", "4A", "4B", "4C", "4D", "4E", 
    "4F", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5A", 
    "5B", "5C", "5D", "5E", "5F", "60", "61", "62", "63", "64", "65", "66", 
    "67", "68", "69", "6A", "6B", "6C", "6D", "6E", "6F", "70", "71", "72", 
    "73", "74", "75", "76", "77", "78", "79", "7A", "7B", "7C", "7D", "7E", 
    "7F", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8A", 
    "8B", "8C", "8D", "8E", "8F", "90", "91", "92", "93", "94", "95", "96", 
    "97", "98", "99", "9A", "9B", "9C", "9D", "9E", "9F", "A0", "A1", "A2", 
    "A3", "A4", "A5", "A6", "A7", "A8", "A9", "AA", "AB", "AC", "AD", "AE", 
    "AF", "B0", "B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8", "B9", "BA", 
    "BB", "BC", "BD", "BE", "BF", "C0", "C1", "C2", "C3", "C4", "C5", "C6", 
    "C7", "C8", "C9", "CA", "CB", "CC", "CD", "CE", "CF", "D0", "D1", "D2", 
    "D3", "D4", "D5", "D6", "D7", "D8", "D9", "DA", "DB", "DC", "DD", "DE", 
    "DF", "E0", "E1", "E2", "E3", "E4", "E5", "E6", "E7", "E8", "E9", "EA", 
    "EB", "EC", "ED", "EE", "EF", "F0", "F1", "F2", "F3", "F4", "F5", "F6", 
    "F7", "F8", "F9", "FA", "FB", "FC", "FD", "FE", "FF" };

Then inside private void SetupLinkLabelHyperlink() method at line 145 I added:
Assembly entLibAssembly = Assembly.GetEntryAssembly();
byte[] entLibPublicKeyToken = new AssemblyName(entLibAssembly.FullName).GetPublicKeyToken();
 
string signedMessage = "";
if(entLibPublicKeyToken != null && entLibPublicKeyToken.Length != 0)
{
    StringBuilder temp = new StringBuilder();
    temp.AppendLine();
    temp.AppendLine("Assembly is signed.");
    temp.Append("Public Token: ");
    for (int i=0; i<entLibPublicKeyToken.Length; i++)
    {
        temp.Append(HEXVALUES[entLibPublicKeyToken[i]]);
    }
    temp.AppendLine();
    signedMessage = temp.ToString();
}
linkLabel1.Text = linkLabel1.Text.Replace("{signed}", signedMessage);
Apr 19, 2007 at 11:10 PM
Edited Apr 20, 2007 at 5:36 PM
Here it is the Matrix.

EntLibConfig.exe EAB (same folder as EntLibConfig.exe) EH/EAB The .dll(s) in the project bin folder Results
A MS Signed MS Signed NS/NS Not Implemented
B MS Signed MS Signed NS/PS Not Implemented
C MS Signed MS Signed NS/MS Not Implemented
D MS Signed MS Signed PS/PS Not Implemented
E MS Signed MS Signed PS/MS Works
F* MS Signed Prog. Signed (mixed ms & prog.) NS/NS Can't Add Exception Block EntLib Config Excpeiontion
G* MS Signed Prog. Signed (mixed ms & prog.) NS/PS Can't Add Exception Block EntLib Config Excpeiontion
H* MS Signed Prog. Signed (mixed ms & prog.) PS/PS Can't Add Exception Block EntLib Config Excpeiontion
I Prog. Signed w/code from my post Prog. Signed NS/NS Not Implemented or Attempting to load none-Signed….
J Prog. Signed w/code from my post Prog. Signed NS/PS Not Implemented or Attempting to load none-Signed….
K Prog. Signed w/code from my post Prog. Signed NS/MS Not Implemented or Attempting to load none-Signed….
L Prog. Signed w/code from my post Prog. Signed PS/PS Works
M Prog. Signed w/code from my post Prog. Signed PS/MS Not Implemented or Attempting to load none-Signed….
N* Prog. Signed w/code from my post MS Signed (mixed ms & prog) NS/NS Can't Add Exception Block EntLib Config Excpeiontion
O* Prog. Signed w/code from my post MS Signed (mixed ms & prog) NS/PS Can't Add Exception Block EntLib Config Excpeiontion
P* Prog. Signed w/code from my post MS Signed (mixed ms & prog) PS/PS Can't Add Exception Block EntLib Config Excpeiontion
Q Not Sigend Not Sigend NS/NS Works
R Not Sigend Not Sigend NS/PS Not Implemented (the coded I added does not work showing vague error)
S Not Sigend Not Sigend NS/MS Not Implemented (the coded I added does not work showing vague error)
T Not Sigend Not Sigend PS/PS Not Implemented (the coded I added does not work showing vague error)
U Not Sigend Not Sigend PS/MS Not Implemented (the coded I added does not work showing vague error)
V* Not Sigend MS Signed (mixed ms & prog) NS/NS Can't Add Exception Block EntLib Config Excpeiontion
W* Not Sigend MS Signed (mixed ms & prog) NS/PS Can't Add Exception Block EntLib Config Excpeiontion
X* Not Sigend MS Signed (mixed ms & prog) PS/PS Can't Add Exception Block EntLib Config Excpeiontion

PS = Programmer Signed
NS = Not Signed
MS = Microsoft Signed
EH = Custom Exception Handler
EAB = Exception Application Block (common.dll, objectbuilder.dll, exceptionhandling.dll)


*Test F-H, N-P, V-X Coppied just EAB dlls from the EH/EAB folder (signed or none-siged) into the bin folder of C:\program files



Each Test was first done by closing EntLibConfig before going to the Next.
Some tests where done with out closing EntLibConfig to see what effects of chaching and how that skews the test base on the path on chooses to load. That results into this:

If you load Test E then test D then D will work. Refernced libraries in EH/EB column are loaded during E and D does not reload them using the same from E working ones.
If you load Test D then test E then E will NOT work. Refernced libraries in EH/EAB column are loaded during D and E does not reload them using the same from D none working ones.




The only thing I see is where the code above does not check the referenced libraries by the project to see if they are signed, as we can see some combination still shows the vague message of not being implemented.

I just got these out of the oven and my head is spinning from watching it cook.

I wanted to post my results, but I'm still going through them.


You will noticed I broke out the dll into two areas (EH/EB and EB) the ones in the BIN folder of entlibconfig and the dlls in the refernces project therefore there can be two versions of EB, one that is loaded with EntLibConfig and the other is in the project bin folder.

Clears as mud?

So not only is there a mix and match issue there is also the perception that it works based on the order of loading assemblies. I could one day load A then B, but the next day load B only to find out that this time it does not work, etc... Then to get the vague message and figure out why it worked one day but not the next, why it is asking to implement something I alread did.






Apr 20, 2007 at 12:11 AM
The matrix has you...

Actually I think you can boil everything down to something much simpler:
  1. At runtime, there must be match between assembly references baked into binaries, assembly references in config and assemblies loaded from disk
  2. In order to load into the config tool, a block or provider's designtime needs to reference the same copy of EnterpriseLibrary.Common.dll and EnterpriseLibrary.Configuration.Design.dll as the tool
  3. In order to use a custom (untyped) provider with a block's designtime, the provider must reference the same block assemblies that the block's designtime assemblies reference.

Again, the issue isn't really to do with whether or not the assemblies are strong-named, it's about whether the assembly identities are the same.

Hopefully this maps to what you've discovered in practice.
Tom
Apr 20, 2007 at 5:21 PM
Yes, the confusing part is the referenced assemblies where one could refernce from folder A and then use tools from B. This is what I believe was going on in 2.0, because we signed the assemblie in-house but other programmers where referncing the binaries from a download 2.0. Then they would run the in-house entlibconfig tool but not be able to load the handler becuase it referenced entlib with a different identity (downloaded). As you can see from my first prototype matrix it did not work becuase the programmers project column is a matrix in of itself. Base on the identity of the programmers assemblie and the indentity of the entlib that is refernces. Is the assembly signed with none sigend references, none-singed w/singned, signed w/signed, etc..

But I still think the real issue is that the Type Selector/EntLibConfig does not do a good job of validating the assembly being loaded, comparing against the cache, etc.. and wether the identity of the assembly being loaded and all of it's referenced assemblies are correct (w or w/out a strong-names). I did not leave out an identity test w/out strong names, so yes indirectly I was testing the idetntities of an assembly regardless of it has a strong name (sgined) or not.

There is an issue with validating, I have seen more and more posts on the forumn, the issue was also part of 2.0, and looking at lengthy blog explaining about. The assemblie identity validation is not in the EntLibConfig tool, but is left up to the one implementing ABs in EntLib therefore making it a house of cards. And having no indication as to if you built your house of cards correctly. If you did not build correctly and there is a mix and match you get an error message to the effect of "You did not Implement the code correctly by not extending XYZ or having atribute ABC."

Here are my arguments for make some improvement, please........

It is like the internet, full of rich contenct like movies, music, news, forumns, provided by the people using tools provided by vendors. But if my router, dns, isp, or some trival piece is flaky and the experience is not good what good is all that rich content? And what if you got an error message saying "You can't connect"? With just a bit more knowledge you know damn well it was the router and if the system was smart enough it would tell ya. So, if the Application Blocks and extending them with my own Handlers, Listeners, Validators is like the internet, and the TypeSelector is like my router or dns, the gateway to my content, then what good is EntLib and AB if when I go to get my content a vague error message pops up about my content not being correct? I can accept the fact that it is wrong but I need more concrete error reproting to fix it. Based on my matrix I see that there are more configurations that can cause problems 12 out 15 that is 80% chase of something going wrong, but with a error message not telling them the right thing. I know that you can't fix the 12 things but one could do a better job of validating and reporting the error. The user can take a path with confusing results, I loaded A then B one day then B the next day and it did not work. I would just hate for people to not use ABs becuase of something as trival as the Type Selector not being robust. I know I stopped using dial up because of the bad experience. And as we can see just a add a bit of code above helps with one area of the matrix so it can't be to difficult. The only challanging one is to intergate and validate against the chached pool to fix the "Load A then B one day and B works, next day load B and no worky." or the inverser revers of that where you Load B first and A does not work.


- guy



Apr 20, 2007 at 9:20 PM
I have a solution, but am unable to implement at this time. The crux of the issue is that even though one get a "Not implemented error" or any other error it is too late and the assembly is loaded into the appdomain. A wrong assemblie is loaded into the current appdomain with out being validated. Once that happens then the assembly cann't be unloaded. Then when you load another assembly with a good library it is too late, the referenced library is already loaded into the appdomain, but is bad and then causes error.

My idea of a solution would be to first load the assembly (w/referenced assemblies) into a seperate AppDomain. Then compare all assembly identities in the TestDomain with the ones in the CurrentAppDomain. If there is a mismatch then don't load otherwise load into CurrentAppDomain.

Of course this is where it all breaks down and I can see why this is not there and like others have asked, and one can see it coming is the Add-In namespace. Or something in regards to making reflections more usable than low level disconnected pieces like AppDomains, Assemble, ShadowCopy, Proxy Object, Real Object, etc...

I will have a solution but I don't think it will be a snippet of code. I'm going to use code I have that does similar stuff with a Plug-In application. I will post it somewhere else and let you all know if you want it.

During testing I further proved the code above does help a little bit, some the issues take two steps to cause a problem, and having that code in place prevents one from taking the first step. Preventing them from taking that step, then step two can't be executed which will then eliminate the Load A then B type issues.


- guy



Apr 21, 2007 at 5:42 AM
Edited Apr 21, 2007 at 5:58 AM
Turns out to be a simple thing once I got the assembly to be loaded inside another AppDomain.

To start I needed to convert the local variables into fields and then add a new one for to hold the assemble for testing before loading into the AppDomain of the config tool.

Again knowing we can't fix the issue we can show a message showing the mismatch. From this one is better informed and knows now that it is an environment error with assemblies and not because of the lack of implementing something. Also because all this is done in another app domain the assembly is not loaded into the primrary appdomain, no need to remove it if it was never added.

This also includes the code from before where we do the assembly test before checking referenced assmblies.

There are two help methods one is to load the assembly into the new AppDomain and to assign the assembly to the new field tempAssemblyForIdentityTesting. The second method is used to check the referenced assemblies in the tempAssemblyForIdentityTesting against the assemblies already loaded in the primary AppDomain.

I'm testing this as I post tred little, but I think one could get the jist from these post that it can be done, but will it be done? Will there be improvements such as these in the tool other than "Not Implemented"?

private string originalAssemblyFileName;
private string referenceDirectory;
private Assembly tempAssemblyForIdentityTesting;
 
private void OnBrowseButtonClick(object sender, EventArgs e)
{
    DialogResult result = this.openFileDialog.ShowDialog();
    if (result == DialogResult.OK)
    {
        try
        {
            originalAssemblyFileName = this.openFileDialog.FileName;
            referenceDirectory = Path.GetDirectoryName(originalAssemblyFileName);
 
            Assembly entLibAssembly = Assembly.GetEntryAssembly();
            byte[] entLibPublicKeyToken = new AssemblyName(entLibAssembly.FullName).GetPublicKeyToken();
            bool entLibSigned = entLibPublicKeyToken != null && entLibPublicKeyToken.Length != 0;
 
            byte[] assemblyPublicKeyToken =
                AssemblyName.GetAssemblyName(originalAssemblyFileName).GetPublicKeyToken();
            bool assemblySigned = assemblyPublicKeyToken != null && assemblyPublicKeyToken.Length != 0;
 
            if (entLibSigned && !assemblySigned)
            {
                DisplayMessageBox(
                    "Attempting to load a None-Signed assembly into a Signed version of EntLibConfig.",
                    "Signed Assemblies Missmatch");
                return;
            }
 
            if (!entLibSigned && assemblySigned)
            {
                DisplayMessageBox("Attempting to load a Signed assembly into a None-Signed EntLibConfig.",
                                  "Signed Assemblies Missmatch");
                return;
            }
 
            // Build a tempory domain and load the assembly there first for
            // the identity of the referenced assemblies.
            AppDomain identityTestDomain = AppDomain.CreateDomain("Identity Validation Domain", null, 
                AppDomain.CurrentDomain.BaseDirectory, AppDomain.CurrentDomain.BaseDirectory, false);
            identityTestDomain.DoCallBack(new CrossAppDomainDelegate(LoadAssemblyForTesting));
            if(!AssemblyIdentityTesting())
            {
                AppDomain.Unload(identityTestDomain);
                return;
            }
            AppDomain.Unload(identityTestDomain);
 
 
            Assembly assembly = null;
            using (AssemblyLoader loaderHook = new AssemblyLoader(originalAssemblyFileName, referenceDirectory))
            {
                
                assembly = Assembly.LoadFrom(loaderHook.CopiedAssemblyPath);
 
                if (!this.selector.LoadTreeView(assembly))
                {
                    DisplayMessageBox(string.Format(CultureInfo.CurrentUICulture, 
                        Resources.NoTypesFoundInAssemblyErrorMessage, assembly.GetName().Name, 
                        this.selector.TypeToVerify.FullName), Resources.NoTypesFoundInAssemblyCaption);
                }
            }
        }
        catch (FileNotFoundException ex)
        {
            DisplayMessageBox(string.Format(CultureInfo.CurrentUICulture, 
                Resources.AssemblyLoadFailedErrorMessage, ex.Message), string.Empty);
            return;
        }
        catch (BadImageFormatException ex)
        {
            DisplayMessageBox(string.Format(CultureInfo.CurrentUICulture, 
                Resources.AssemblyLoadFailedErrorMessage, ex.Message), string.Empty);
            return;
        }
        catch(ReflectionTypeLoadException ex)
        {
            DisplayMessageBox(string.Format(CultureInfo.CurrentUICulture, 
                Resources.EnumTypesFailedErrorMessage, ex.Message), string.Empty);
        }
    }
}
 
private bool AssemblyIdentityTesting()
{
    AssemblyName[] referncedAssemblie = tempAssemblyForIdentityTesting.GetReferencedAssemblies();
    AppDomain currentDomain = AppDomain.CurrentDomain;
 
    Assembly[] assembliesInCurrentdomain = currentDomain.GetAssemblies();
 
    foreach (AssemblyName assemblyToTest in referncedAssemblie)
    {
        foreach (Assembly assemblyInCurrentDomain in assembliesInCurrentdomain)
        {
            if (assemblyInCurrentDomain.GetName().Name == assemblyToTest.Name)
                if (assemblyInCurrentDomain.FullName != assemblyToTest.FullName)
                {
                    string message = string.Format(
                        "There is an identity mismatch between the Enterprise Library referenced " +
                        "in the assembly you are loading and the Entprise Library referenced by " +
                        "the Configuration tool.\n\n  " +
                        "Enterprise Library assembly refernced by the assembly being loaded:\n{0}\n\n" +
                        "Enterprise Library assembly Refernced by the Configuration Tool:\n{1}\n ",
                        assemblyToTest.FullName, assemblyInCurrentDomain.FullName);
                    DisplayMessageBox(message, "Name Identity Mismatch");
                    return false;
                }
        }
    }
    return true;
}
 
private void LoadAssemblyForTesting()
{
    using (AssemblyLoader loaderHook = new AssemblyLoader(originalAssemblyFileName, referenceDirectory))
    {
        tempAssemblyForIdentityTesting = Assembly.LoadFrom(loaderHook.CopiedAssemblyPath);
    }
}