|
This section of the chapter deals with AppDomains. AppDomains are essentially Common Language Runtime sandboxes; that is, separated memory spaces within a runtime host. Quite a few things can be done with AppDomains. Most of the time, programmers are unaware of the fact that their code is executing within an AppDomain and as such miss out on some very powerful features of the .NET Framework. Introduction to AppDomainsAn AppDomain is both an in-memory construct used by the Common Language Runtime for isolating applications from each other, as well as a class provided by the .NET Framework. Tables 12.2 and 12.3 describe some of the more notable and useful methods and properties belonging to the AppDomain class.
Programming with AppDomainsWith that information in hand, you can now take a look at an example of programming with AppDomains. This next sample will show you how to use Getdata and SetData to store and retrieve values that have the same scope as the AppDomain. In addition, you'll see how to create new AppDomains and load instances of classes into domains other than the default. The first thing to do is to add a new method to the AssemblyTool class. It is shown highlighted in Listing 12.5. Note the addition of the new namespaces and the fact that AssemblyTool now inherits from MarshalByRefObject. Don't worry about this class; you'll see plenty of it in this book's coverage of remoting. For now, think of it as a marker that tells the Common Language Runtime to share the same object between AppDomains instead of creating a serialized copy. If the AssemblyTool class didn't have this marker, the method in Listing 12.5 would not work properly. Listing 12.5. The Modified AssemblyTool Classusing System; using System.Runtime.Remoting; using System.Resources; using System.IO; using System.Xml; using System.Reflection; using System.Text; namespace SAMS.CSharpUnleashed.Chapter12.AssemblyIntro { /// <summary> /// This is the AssemblyTool sample class /// </summary> public class AssemblyTool : MarshalByRefObject { private static XmlDocument doc = null; public AssemblyTool() { } public static string GetAssemblyInfo() { // gets the Assembly in which this code is executing, not // necessarily the Assembly of the main executable (EX Assembly thisAssembly = Assembly.GetExecutingAssembly(); AssemblyName thisName = thisAssembly.GetName(); StringBuilder sb = new StringBuilder(); sb.AppendFormat("Assembly Name: {0}\n", thisAssembly.FullName); sb.AppendFormat("Assembly Version: {0}\n", thisName.Version.ToString()); sb.AppendFormat("Assembly Culture: {0}\n", thisName.CultureInfo.ToString()); return sb.ToString(); } private static void LoadEmbeddedDoc() { Assembly thisAssembly = Assembly.GetExecutingAssembly(); Stream s = thisAssembly.GetManifestResourceStream( "SAMS.CSharpUnleashed.Chapter12.AssemblyIntro.EmbeddedData.xml"); doc = new XmlDocument(); doc.Load( s ); } public static string GetDataNodeValue( int nodeId ) { if (doc == null) LoadEmbeddedDoc(); XmlNode node = doc.SelectSingleNode( string.Format("//DataNode[@id='{0}']", nodeId ) ); if (node != null) return node.InnerText; else return "No node found"; } public static string GetResourceString( string id ) { ResourceManager rm = new ResourceManager( "SAMS.CSharpUnleashed.Chapter12.AssemblyIntro.Strings", Assembly.GetExecutingAssembly()); return rm.GetString( id ); } public string GetAppDomainInfo() { AppDomain ad = AppDomain.CurrentDomain; StringBuilder sb = new StringBuilder(); sb.AppendFormat("------\nAppDomain: {0}\n", ad.FriendlyName); sb.AppendFormat("\tHash Code: {0}\n", ad.GetHashCode()); if (ad.GetData("MYVALUE") != null) sb.AppendFormat("\tStored Value: {0}\n", ad.GetData("MYVALUE")); foreach (Assembly asm in ad.GetAssemblies()) { sb.AppendFormat("\tLoaded Assembly: {0}\n", asm.GetName().Name); } return sb.ToString(); } } } The key piece here is the GetAppDomainInfo method. It uses some reflection methods to obtain information about the current AppDomain, including its name, hashcode, and even the list of all assemblies currently loaded into that AppDomain. Also note the use of Getdata to retrieve a named value. We'll be using SetData to set that value in the Harness project next. Make sure that the AssemblyIntro project compiles, add a using statement for System.Runtime.Remoting at the top of Class1.cs, and then add the following code to the end of Class1 in the Harness project: // experiment with an AppDomain AssemblyTool at = new AssemblyTool(); AppDomain.CurrentDomain.SetData("MYVALUE", 1024); Console.WriteLine( at.GetAppDomainInfo()); AppDomain ad = AppDomain.CreateDomain("SecondDomain", null, (AppDomainSetup)null); Console.WriteLine("New domain has {0} Assemblies loaded.", ad.GetAssemblies().Length); ad.SetData("MYVALUE", 42); ObjectHandle handle = ad.CreateInstance( "SAMS.CSharpUnleashed.Chapter12.AssemblyIntro", "SAMS.CSharpUnleashed.Chapter12.AssemblyIntro.AssemblyTool"); AssemblyTool at2 = (AssemblyTool)handle.Unwrap(); Console.WriteLine( at2.GetAppDomainInfo()); Console.WriteLine("New domain has {0} Assemblies loaded.", ad.GetAssemblies().Length); In the preceding code, an instance of AssemblyTool is created in the current AppDomain. Then, using the new method, the AppDomain information is displayed. Then a new AppDomain is created and the CreateInstance method is used to create a new instance of the AssemblyTool class in the new AppDomain. Finally, the instance from the second domain is used to display the AppDomain information. If everything works properly, you should see very clearly that the two AppDomains are very distinct. They each have their own list of loaded assemblies, and therefore have their own unique lists of loaded types. Here is the output from the new section of code added to the test Harness application: AppDomain: Harness.exe Hash Code: 2 Stored Value: 1024 Loaded Assembly: mscorlib Loaded Assembly: Harness Loaded Assembly: SAMS.CSharpUnleashed.Chapter12.AssemblyIntro Loaded Assembly: System.Xml Loaded Assembly: System Loaded Assembly: SAMS.CSharpUnleashed.Chapter12.AssemblyIntro.resources Loaded Assembly: SAMS.CSharpUnleashed.Chapter12.AssemblyIntro.resources New domain has 1 Assemblies loaded. ------ AppDomain: SecondDomain Hash Code: 8 Stored Value: 42 Loaded Assembly: mscorlib Loaded Assembly: SAMS.CSharpUnleashed.Chapter12.AssemblyIntro Loaded Assembly: System.Xml New domain has 3 Assemblies loaded. There is quite a bit of interesting information available here. The default domain (named Harness.exe) has several assemblies loaded; in addition, you can see two .resources assemblies loaded. These are the satellite assemblies built in the previous section of this chapter. When the new domain is created, it contains only one loaded assembly: mscorlib. After the code loads the AssemblyTool type into the new AppDomain, you can see that it loads SAMS.CSharpUnleaded.Chapter12.AssemblyIntro (the assembly in which the AssemblyTool class resides) and System.Xml. This is because for the Common Language Runtime to be able to properly handle the AssemblyTool type, it needs metadata from the System.Xml assembly. |
|