mcts self paced training kit exam 70-536 microsoft net framework 3.5 application development foundation phần 5 ppsx

82 348 0
mcts self paced training kit exam 70-536 microsoft net framework 3.5 application development foundation phần 5 ppsx

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

296 Chapter Threading it increments the value as it was before Thread2 updated it—thus completing the increment operation but rewriting the value with 21 In this scenario, two increment operations resulted in incrementing the value only one time As the previous code sample demonstrates, this can and does happen in real-world programming environments, and mathematical errors such as this can be disastrous To correct the problem, replace the number += operation in myNum.AddOne with Interlocked.Increment(ref number), and then run the application again This time, the results are perfect because Interlocked.Increment does not allow another thread to interrupt the increment operation IMPORTANT Multithreading Best Practices For more information about how to minimize problems when writing multithreaded applications, read “Managed Threading Best Practices” at the MSDN Library (http://msdn.microsoft.com/en-us/ library/1c9txz50.aspx) Waiting for Threads to Complete Often, your application’s primary thread must wait for background threads to complete before continuing If you are waiting on a single thread, you can simply call Thread.Join, which halts processing until the thread terminates If you need to wait for multiple threads to complete, use the WaitHandle.WaitAll static method with an AutoResetEvent array The following code sample demonstrates this In this example, the custom ThreadInfo class provides everything that the background thread needs to execute; namely, an integer that represents the number of milliseconds to wait and an AutoResetEvent instance that it can set (by calling AutoResetEvent.Set) when processing is complete: ' VB ' Define an array with three AutoResetEvent WaitHandles Dim waitHandles As AutoResetEvent() = New AutoResetEvent() _ {New AutoResetEvent(False), _ New AutoResetEvent(False), _ New AutoResetEvent(False)} Sub Main() ' Queue up tasks on different threads; wait until all tasks are ' completed ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf DoTask), _ New ThreadInfo(3000, waitHandles(0))) ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf DoTask), _ Lesson 2: Managing Threads New ThreadInfo(2000, waitHandles(1))) ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf DoTask), _ New ThreadInfo(1000, waitHandles(2))) WaitHandle.WaitAll(waitHandles) Console.WriteLine("Main thread is complete.") Console.ReadKey() End Sub Sub DoTask(ByVal state As Object) Dim ti As ThreadInfo = DirectCast(state, ThreadInfo) Thread.Sleep(ti.ms) Console.WriteLine("Waited for " + ti.ms.ToString() + " ms.") ti.are.Set() End Sub Class ThreadInfo Public are As AutoResetEvent Public ms As Integer Public Sub New(ByVal _ms As Integer, ByVal _are As AutoResetEvent) ms = _ms are = _are End Sub End Class // C# // Define an array with three AutoResetEvent WaitHandles static AutoResetEvent[] waitHandles = new AutoResetEvent[] { new AutoResetEvent(false), new AutoResetEvent(false), new AutoResetEvent(false) }; static void Main() { // Queue up tasks on different threads; wait until all tasks are // completed ThreadPool.QueueUserWorkItem(new WaitCallback(DoTask), new ThreadInfo(3000, waitHandles[0])); ThreadPool.QueueUserWorkItem(new WaitCallback(DoTask), new ThreadInfo(2000, waitHandles[1])); ThreadPool.QueueUserWorkItem(new WaitCallback(DoTask), new ThreadInfo(1000, waitHandles[2])); WaitHandle.WaitAll(waitHandles); Console.WriteLine("Main thread is complete."); Console.ReadKey(); } static void DoTask(Object state) { ThreadInfo ti = (ThreadInfo)state; Thread.Sleep(ti.ms); 297 298 Chapter Threading Console.WriteLine("Waited for " + ti.ms.ToString() + " ms."); ti.are.Set(); } class ThreadInfo { public AutoResetEvent are; public int ms; public ThreadInfo(int _ms, AutoResetEvent _are) { ms = _ms; are = _are; } } If you run that code, you see the following output: Waited for 1000 ms Waited for 2000 ms Waited for 3000 ms Main thread is complete Notice that the message “Main thread is complete” displays only after all three threads have completed, indicating that the Main thread waited for the threads before continuing If you comment out the call to WaitHandle.WaitAll, you see the following output, which indicates that the Main thread continued to process without waiting for the background threads: Main thread is complete Waited for 1000 ms Waited for 2000 ms Waited for 3000 ms You can also call WaitHandle.WaitAny, which waits for the first thread to return In this example, replacing WaitHandle.WaitAll with WaitHandle.WaitAny produces the following output: Waited for 1000 ms Main thread is complete Waited for 2000 ms Waited for 3000 ms In this example, notice that the Main method in Visual Basic has the MTAThread attribute Without it, the Main method would be started as a single-threaded apartment (STA) thread STA is designed to be used in single-threaded environments Only multithreaded apartment (MTA) threads support calling WaitHandle.WaitAll C# starts the main method as an MTA thread by default, and thus it does not require the MTAThread attribute (although you could add it if you wanted; it’s just unnecessary because it’s the default setting) Lesson 2: Managing Threads 299 Lab: Manage Threads In this lab, you will expand the application that you created in Lesson so that it properly waits for threads to complete Then, you convert it to use multiple Thread instances rather than calling ThreadPool.QueueUserWorkItem In this exercise, you must update the application you created in Lesson so that it waits for all threads to complete before displaying the results Exercise 1: Wait for Threads to Complete Navigate to the \\Chapter07\Lesson2\Exercise1\Partial folder and open either the C# version or the Visual Basic NET version of the solution file Alternatively, you can continue working from the project you created for Lesson Build and run the application Notice that the program attempts to display the time elapsed while retrieving the five Web pages, but it displays an incorrect value because it does not wait for the GetPage threads to complete First, create an array of AutoResetEvent objects within the Main method, with one element for every Uniform Resource Locator (URL) The following declaration works, but it must be placed after the urls variable is declared: ' VB Dim waitHandles As AutoResetEvent() = New AutoResetEvent( _ urls.Length - 1) {} // C# AutoResetEvent[] waitHandles = new AutoResetEvent[urls.Length]; To use the waitHandles array, you must pass one element of the array to each thread To that, you must pass a single object to the method being called, and that object must contain both the element of the waitHandles array and the string that will be used as the URL Therefore, you must create a new class that contains two members: an instance of AutoResetEvent and a string for the URL The following demonstrates how to create this class: ' VB Class ThreadInfo Public url As String Public are As AutoResetEvent Public Sub New(ByVal _url As String, ByVal _are As AutoResetEvent) url = _url are = _are End Sub End Class 300 Chapter Threading // C# class ThreadInfo { public string url; public AutoResetEvent are; public ThreadInfo(string _url, AutoResetEvent _are) { url = _url; are = _are; } } Update the GetPage method to cast the data object to an instance of ThreadInfo, as shown here: ' VB Sub GetPage(ByVal data As Object) ' Cast the object to a ThreadInfo Dim ti As ThreadInfo = DirectCast(data, ThreadInfo) ' Request the URL Dim wr As WebResponse = WebRequest.Create(ti.url).GetResponse() ' Display the value for the Content-Length header Console.WriteLine(ti.url + ": " + wr.Headers("Content-Length")) wr.Close() ' Let the parent thread know the process is done ti.are.Set() End Sub // C# static void GetPage(object data) { // Cast the object to a ThreadInfo ThreadInfo ti = (ThreadInfo)data; // Request the URL WebResponse wr = WebRequest.Create(ti.url).GetResponse(); // Display the value for the Content-Length header Console.WriteLine(ti.url + ": " + wr.Headers["Content-Length"]); wr.Close(); // Let the parent thread know the process is done ti.are.Set(); } Lesson 2: Managing Threads 301 Next, update the foreach loop in the Main method to create an instance of the ThreadInfo class and pass it to the GetPage method, as demonstrated here: ' VB Dim i As Integer = For Each url As String In urls waitHandles(i) = New AutoResetEvent(False) Dim ti As New ThreadInfo(url, waitHandles(i)) ThreadPool.QueueUserWorkItem(AddressOf GetPage, ti) i += Next // C# int i = 0; foreach (string url in urls) { waitHandles[i] = new AutoResetEvent(false); ThreadInfo ti = new ThreadInfo(url, waitHandles[i]); ThreadPool.QueueUserWorkItem(GetPage, ti); i++; } Finally, call the static WaitHandle.WaitAll method before displaying the elapsed time, as demonstrated here: ' VB WaitHandle.WaitAll(waitHandles) // C# WaitHandle.WaitAll(waitHandles); If you are using Visual Basic, add the MTAThread attribute to the Main method (This is not required in C#, because it is the default.) ' VB Sub Main() Build and run the application Notice that it correctly waits until all threads have returned before displaying the elapsed time Bug fixed! In this exercise, you will update the application that you created in Lesson to create a Thread object and return results to a callback method rather than calling ThreadPool.QueueUserWorkItem Exercise 2: Pass Values Back from Threads Navigate to the \\Chapter07\Lesson2\Exercise2\Partial folder and open either the C# version or the Visual Basic NET version of the solution file Alternatively, you can continue working from the project you created for Lesson 2, Exercise 302 Chapter Threading To pass data to a method when you create an instance of Thread, you need a nonstatic method Therefore, you should replace the GetPage method and ThreadInfo class with a class containing both parameters and a method that the thread will run, as shown here: ' VB Public Class PageSize Public url As String Public are As AutoResetEvent Public bytes As Integer Public Sub New(ByVal _url As String, ByVal _are As AutoResetEvent) url = _url are = _are End Sub Public Sub GetPageSize() ' Request the URL Dim wr As WebResponse = WebRequest.Create(url).GetResponse() bytes = Integer.Parse(wr.Headers("Content-Length")) ' Display the value for the Content-Length header Console.WriteLine(url + ": " + bytes.ToString()) wr.Close() ' Let the parent thread know the process is done are.Set() End Sub End Class // C# public class PageSize { public string url; public AutoResetEvent are; public int bytes; public PageSize(string _url, AutoResetEvent _are) { url = _url; are = _are; } public void GetPageSize() { // Request the URL WebResponse wr = WebRequest.Create(url).GetResponse(); bytes = int.Parse(wr.Headers["Content-Length"]); Lesson 2: Managing Threads 303 // Display the value for the Content-Length header Console.WriteLine(url + ": " + bytes.ToString()); wr.Close(); // Let the parent thread know the process is done are.Set(); } } Threads can return values by calling a callback method that you pass to the thread as a parameter To create a callback method, write the method and create a matching delegate In this case, we want the thread to return both the URL and the bytes in the Web page, so we can simply pass an instance of the PageSize class For the purpose of this example, the callback method can simply display the output to the console The following code creates the method and the callback: ' VB ' The callback method must match the signature of the callback delegate Sub ResultCallback(ByVal ps As PageSize) Console.WriteLine("{0}: {1}", ps.url, ps.bytes.ToString()) End Sub ' Delegate that defines the signature for the callback method Delegate Sub ResultDelegate(ByVal ps As PageSize) // C# // The callback method must match the signature of the callback delegate static void ResultCallback(PageSize ps) { Console.WriteLine("{0}: {1}", ps.url, ps.bytes.ToString()); } // Delegate that defines the signature for the callback method public delegate void ResultDelegate(PageSize ps); Update the PageSize class to accept the callback method as a parameter in the constructor, and then store the callback value, as shown here (you should not delete the GetPageSize method): ' VB Class PageSize Public url As String Public are As AutoResetEvent Public bytes As Integer ' Delegate used to execute the callback method when the task is ' complete Private callback As ResultDelegate 304 Chapter Threading Public Sub New(ByVal _url As String, ByVal _are As AutoResetEvent, _ ByVal _callback As ResultDelegate) url = _url are = _are callback = _callback End Sub End Class // C# public class PageSize { public string url; public AutoResetEvent are; public int bytes; // Delegate used to execute the callback method when the task is // complete private ResultDelegate callback; public PageSize(string _url, AutoResetEvent _are, ResultDelegate _callback) { url = _url; are = _are; callback = _callback; } } Next, update the PageSize class to store the callback method, and accept the callback as a parameter for the constructor In addition, comment out the line in the GetPageSize method that displays the URL and page size to the console and instead call the callback method, passing the current PageSize instance The following code demonstrates how to update the PageSize class (changes are shown in bold): ' VB Public Sub GetPageSize() ' Request the URL Dim wr As WebResponse = WebRequest.Create(url).GetResponse() bytes = Integer.Parse(wr.Headers("Content-Length")) ' Display the value for the Content-Length header ''''' Console.WriteLine(url + ": " + bytes.ToString()); wr.Close() callback(Me) ' Let the parent thread know the process is done are.[Set]() End Sub Lesson 2: Managing Threads 305 // C# public void GetPageSize() { // Request the URL WebResponse wr = WebRequest.Create(url).GetResponse(); bytes = int.Parse(wr.Headers["Content-Length"]); // Display the value for the Content-Length header ///// Console.WriteLine(url + ": " + bytes.ToString()); wr.Close(); callback(this); // Let the parent thread know the process is done are.Set(); } Finally, update the foreach loop in the Main method to create and start a new Thread instance rather than calling ThreadPool.QueueUserWorkItem Pass the callback method to the PageSize constructor, as shown here: ' VB For Each url As String In urls waitHandles(i) = New AutoResetEvent(False) Dim ps As New PageSize(url, waitHandles(i), _ New ResultDelegate(AddressOf ResultCallback)) Dim t As New Thread(New ThreadStart(AddressOf ps.GetPageSize)) t.Start() i += Next // C# foreach (string url in urls) { waitHandles[i] = new AutoResetEvent(false); PageSize ps = new PageSize(url, waitHandles[i], new ResultDelegate(ResultCallback)); Thread t = new Thread(new ThreadStart(ps.GetPageSize)); t.Start(); i++; } Build and run the application Although it functions exactly the same as it did in Lesson 2, Exercise 1, the results are now being processed by a callback method In the real world, this is a much more useful scenario—background threads almost always need to return results to the foreground thread Lesson 1: Configuring Applications 363 virtual host and not use the standard config file After running the application, it generates the following file, which contains the name and value pair: This file also demonstrates how you would create an application configuration file manually Using the XML format, create opening and closing tags for a section Within the section, create opening and closing tags for an section Then, create elements that define key and value properties Reading Application Configuration Settings You can read application configuration settings using the static ConfigurationManager AppSettings name/value collection For example, the following code sample displays all application configuration settings to the console: ' VB For i As Integer = To ConfigurationManager.AppSettings.Count - Console.WriteLine("{0}: {1}", _ ConfigurationManager.AppSettings.AllKeys(i), _ ConfigurationManager.AppSettings(i)) Next // C# for (int i = 0; i < ConfigurationManager.AppSettings.Count; i++) { Console.WriteLine("{0}: {1}", ConfigurationManager.AppSettings.AllKeys[i], ConfigurationManager.AppSettings[i]); } You can also access specific settings using the key name For example, assume you have the following config file: 364 Chapter Installing and Configuring Applications The following code would display the value associated with the Greeting key (“Hello, world!”): ' VB Console.WriteLine(ConfigurationManager.AppSettings("Greeting")) // C# Console.WriteLine(ConfigurationManager.AppSettings["Greeting"]); Using Connection Strings One of the most common uses of application settings is to define a database connection string Connection strings define how a client application connects to a back-end database By storing connection strings in a configuration file, systems administrators can define the connection string by editing the configuration file It’s important for systems administrators to be able to this because database servers might change names, locations, or credentials To access connection strings, use the ConfigurationManager.ConnectionStrings static collection similar to the way you accessed ConfigurationManager.AppSettings However, although AppSettings is a standard NameValueCollection, ConnectionStrings is a ConnectionStringSettingsCollection The three most useful properties of the ConnectionStringSettings class are Name (which defines the name of the connection), ProviderName (which defines the type of database connection), and ConnectionString (which defines how the client connects to the server) For example, consider the following connection string, which could be defined either in an application’s config file or in the Machine.config file (The connection string has been formatted to fit on the printed page, but must appear on a single line in the file.) The following code sample accesses that connection string by name (the most common real-world use) and then displays all connection strings: ' VB ' Display a specific connection string Console.WriteLine(ConfigurationManager.ConnectionStrings( _ "LocalSqlServer").ConnectionString) Lesson 1: Configuring Applications 365 ' Display all connection strings Dim connections As ConnectionStringSettingsCollection = _ ConfigurationManager.ConnectionStrings For Each connection As ConnectionStringSettings In connections Console.WriteLine("Name: {0}", connection.Name) Console.WriteLine("Connection string: {0}", _ connection.ConnectionString) Console.WriteLine("Provider: {0}", connection.ProviderName) Console.WriteLine("Source: {0}", _ connection.ElementInformation.Source) Next // C# // Display a specific connection string Console.WriteLine(ConfigurationManager.ConnectionStrings[ "LocalSqlServer"].ConnectionString); // Display all connection strings ConnectionStringSettingsCollection connections = ConfigurationManager.ConnectionStrings; foreach (ConnectionStringSettings connection in connections) { Console.WriteLine("Name: {0}", connection.Name); Console.WriteLine("Connection string: {0}", connection.ConnectionString); Console.WriteLine("Provider: {0}", connection.ProviderName); Console.WriteLine("Source: {0}", connection.ElementInformation.Source); } Once you create a ConnectionStringSettings object, you can examine the ProviderName parameter to determine which type of database connection object to create The following code sample demonstrates how to use ProviderName to create a database platform-specific DbConnection object using a ConnectionStringsSettings object named connection: ' VB Dim db As DbConnection = Nothing Select Case connection.ProviderName Case "System.Data.SqlClient" db = New SqlConnection(connection.ConnectionString) Exit Select Case "System.Data.OleDb" db = New OleDbConnection(connection.ConnectionString) Exit Select Case "System.Data.Odbc" db = New OdbcConnection(connection.ConnectionString) Exit Select Case "System.Data.OracleClient" db = New OracleConnection(connection.ConnectionString) Exit Select End Select 366 Chapter Installing and Configuring Applications // C# DbConnection db = null; switch (connection.ProviderName) { case "System.Data.SqlClient": db = new SqlConnection(connection.ConnectionString); break; case "System.Data.OleDb": db = new OleDbConnection(connection.ConnectionString); break; case "System.Data.Odbc": db = new OdbcConnection(connection.ConnectionString ); break; case "System.Data.OracleClient": db = new OracleConnection(connection.ConnectionString); break; } Reading Machine Configuration Settings Typically, you not need to read machine configuration settings directly However, when you need to, you can call the ConfigurationManager.OpenMachineConfiguration method to create a Configuration object representing the Machine.config file For example, the Machine.config file contains a section that describes cryptographic technologies available for protecting configuration data The following code shows a typical section in the Machine.config file: Lesson 1: Configuring Applications 367 As you can see, that section defines two providers (RsaProtectedConfigurationProvider and DataProtectionConfigurationProvider), and defines RsaProtectionConfigurationProvider as the default provider You can access the default provider, or any aspect of the configured providers, by following these steps: Retrieve the machine configuration Call Configuration.GetSection to retrieve the section Cast the ConfigurationSection object returned by Configuration.GetSection to a class specific to the configuration section you are accessing In the case of , you need to use the ProtectedConfigurationSection class Access the properties of the ProtectedConfigurationSection class, or whichever ConfigurationSection type you are using The following code sample displays the default protection configuration provider and then displays the description of DataProtectionConfigurationProvider: ' VB ' Open the Machine.config file Dim machineSettings As Configuration = _ ConfigurationManager.OpenMachineConfiguration() ' Retrieve the configProtectedData section Dim pcs As ProtectedConfigurationSection = _ DirectCast(machineSettings.GetSection("configProtectedData"), _ ProtectedConfigurationSection) ' Display the default provider Console.WriteLine(pcs.DefaultProvider) ' Display the description for the DataProtectionConfigurationProvider Console.WriteLine(pcs.Providers( _ "DataProtectionConfigurationProvider").Parameters("description")) // C# // Open the Machine.config file Configuration machineSettings = ConfigurationManager.OpenMachineConfiguration(); // Retrieve the configProtectedData section ProtectedConfigurationSection pcs = (ProtectedConfigurationSection)machineSettings.GetSection( "configProtectedData"); // Display the default provider Console.WriteLine(pcs.DefaultProvider); // Display the description for the DataProtectionConfigurationProvider Console.WriteLine(pcs.Providers[ "DataProtectionConfigurationProvider"].Parameters["description"]); 368 Chapter Installing and Configuring Applications Each configuration section has a unique class To determine which class a configuration section uses, call ConfigurationManager.OpenMachineConfiguration().GetSection( "").ElementInformation.Type.ToString Creating Custom Sections To allow you to access custom application configuration settings using strong types, you can create custom classes There are two ways to this: by implementing the IConfigurationSectionHandler interface and by deriving a class from ConfigurationSection Exam Tip The IConfigurationSectionHandler interface is included in this book only because it might be covered on the 70-536 certification exam; it is deprecated in the NET Framework version 2.0 and later Creating Custom Sections Using IConfigurationSectionHandler Just as there are unique classes for different sections in the Machine.config file, you can create unique classes for custom sections in your application’s config file by creating a class that inherits from the IConfigurationSectionHandler interface When implementing the IConfigurationSectionHandler interface, you only need to create a constructor and implement the Create method Of the three parameters required by the Create method, you typically need to access only the third parameter, an object of the type System.Xml.XmlNode You can call XmlNode.InnerText to access the data stored within the element For example, consider the following simple Console application, which reads two parameters from a custom section in the application’s config file and outputs them to the console Notice that within the Main method, custom settings are accessed using strong types, which is more elegant than parsing text from application settings The CustomConfigHandler class implements the IConfigurationSectionHandler interface, and the CustomConfigHandler.Create method reads the settings from the appropriate section of the config file and stores the values in a new instance of the custom MySettings class: ' VB Public Class MySettings Public lastUser As String Public lastNumber As Integer Public Sub New() End Sub End Class Lesson 1: Configuring Applications Public Class CustomConfigHandler Implements IConfigurationSectionHandler Function Create(ByVal parent As Object, _ ByVal configContext As Object, ByVal section As Xml.XmlNode) _ As Object Implements IConfigurationSectionHandler.Create Dim settings As New MySettings() settings.lastUser = _ section.SelectSingleNode("lastUser").InnerText settings.lastNumber = _ Integer.Parse(section.SelectSingleNode("lastNumber").InnerText) Return settings End Function End Class Module Module1 Sub Main() Dim settings As MySettings = _ DirectCast(ConfigurationManager.GetSection( _ "customSettings"), MySettings) Console.WriteLine(settings.lastUser) Console.WriteLine(settings.lastNumber) End Sub End Module // C# namespace ConfigApp { public class MySettings { public string lastUser; public int lastNumber; public MySettings() { } } public class CustomConfigHandler : IConfigurationSectionHandler { public CustomConfigHandler() { } public object Create(object parent, object configContext, System.Xml.XmlNode section) { MySettings settings = new MySettings(); settings.lastUser = section.SelectSingleNode("lastUser").InnerText; settings.lastNumber = int.Parse(section.SelectSingleNode( "lastNumber").InnerText); return settings; } } 369 370 Chapter Installing and Configuring Applications class Program { static void Main(string[] args) { MySettings settings = (MySettings)ConfigurationManager.GetSection( "customSettings"); Console.WriteLine(settings.lastUser); Console.WriteLine(settings.lastNumber); } } } The following config file demonstrates how to structure a custom configuration section Notice the section, which declares the section name (in the name property) and the method that implements IConfigurationSectionHandler and the assembly name (in the type property) Then, notice the custom configuration section, with elements for each custom value Examine this configuration file and how the CustomConfigHandler.Create method reads the values (The section is included only to demonstrate that a config file can contain both custom settings and standard application settings.) Tony 32 Exam Tip Although IConfigurationSectionHandler is covered on the 70-536 exam objectives, it is deprecated in NET Framework version 2.0 and later Instead, you should use ConfigurationSection, described in the next section Creating Custom Sections Using ConfigurationSection Deriving a custom class from ConfigurationSection is the preferred way to implement custom configuration sections in NET Framework version 2.0 and later ConfigurationSection allows you to declare properties that the Common Language Runtime (CLR) Lesson 1: Configuring Applications 371 automatically populates based on the data in the config file, saving you the trouble of manually parsing XML elements You can also use attributes to configure default values, validators, and other requirements for properties The following code sample provides similar functionality to the IConfigurationSectionHandler example: ' VB Public Class MyHandler Inherits ConfigurationSection Public Sub New() End Sub _ _ Public ReadOnly Property LastUser() As String Get Return CStr(Me("lastUser")) End Get End Property _ Public Property LastNumber() As Integer Get Return CStr(Me("lastNumber")) End Get Set(ByVal value As Integer) Me("lastNumber") = value End Set End Property End Class Module Module1 Sub Main() Dim settings As MyHandler = _ DirectCast(ConfigurationManager.GetSection( _ "customSettings"), MyHandler) Console.WriteLine(settings.LastUser) Console.WriteLine(settings.LastNumber.ToString()) End Sub End Module // C# namespace ConfigApp { public class MyHandler:ConfigurationSection { public MyHandler() { } 372 Chapter Installing and Configuring Applications [ConfigurationProperty("lastUser", DefaultValue = "User", IsRequired = true)] [StringValidator(InvalidCharacters = "~!@#$%^&*()[]{}/;'\"|\\", MinLength = 1, MaxLength = 60)] public string LastUser { get { return (string)this["lastUser"]; } set { this["lastUser"] = value; } } [ConfigurationProperty("lastNumber")] public int LastNumber { get { return (int)this["lastNumber"]; } set { this["lastNumber"] = value; } } } class Program { static void Main(string[] args) { MyHandler settings = (MyHandler)ConfigurationManager.GetSection( "customSettings"); Console.WriteLine(settings.LastUser); Console.WriteLine(settings.LastNumber); } } } This code sample requires a slightly modified config file, which declares the custom values as attributes: Lesson 1: Configuring Applications 373 Real World Tony Northrup Windows Vista includes User Account Control (UAC), which limits the privileges of the currently logged-on user Even if your account is a member of the Administrators group, programs run in the security context of a standard user by default This can cause problems when updating a config file after installing a NET Framework application because most applications are installed into the %Program Files% folder, which standard users not have permission to update This is a problem only if users need to update the config file after installation—setup typically has administrative privileges, and systems administrators will still be able to update the config file manually To allow users to update settings, create a settings file in the user’s application data folder This folder is defined by the %AppData% environment variable Although the 70-536 exam won’t test your knowledge of standard user privileges, you’ll definitely run into problems when developing applications in the real world Lab: Persistently Storing Configuration Settings In this lab, you will store data to your application’s config file and read it when the application starts Exercise: Reading and Writing Application Configuration Settings In this exercise, you will open an existing WPF application and add functionality to save and read settings Navigate to the \Chapter09\Lesson1\Exercise1\Partial folder and open either the C# version or the Visual Basic NET version of the solution file Add a project reference to the System.configuration.dll file Add the System.Collections.Specialized and System.Configuration namespaces to your Window1 code file 374 Chapter Installing and Configuring Applications Add a handler for the Save Settings button’s Click event Write code to open the application’s configuration file, remove previous settings for the Title and Name settings, add settings based on the values in the titleTextBox and numberSlider controls, and then write the settings to the configuration file The following code demonstrates how to this: ' VB Dim config As Configuration = _ ConfigurationManager.OpenExeConfiguration( _ ConfigurationUserLevel.None) config.AppSettings.Settings.Remove("Title") config.AppSettings.Settings.Add("Title", titleTextBox.Text) config.AppSettings.Settings.Remove("Number") config.AppSettings.Settings.Add("Number", _ numberSlider.Value.ToString()) config.Save(ConfigurationSaveMode.Modified) // C# Configuration config = ConfigurationManager.OpenExeConfiguration( ConfigurationUserLevel.None); config.AppSettings.Settings.Remove("Title"); config.AppSettings.Settings.Add("Title", titleTextBox.Text); config.AppSettings.Settings.Remove("Number"); config.AppSettings.Settings.Add("Number", numberSlider.Value.ToString()); config.Save(ConfigurationSaveMode.Modified); In the Extensible Application Markup Language (XAML) file, create a handler for the window’s Loaded event For example, you could add the line shown here in bold to the XAML element: In the Window.Loaded event handler, write code that determines whether the Title and Number application settings have been defined If they have been, define the window’s Title property and the titleTextBox.Text property using the Title application setting Then, define the value of numberSlider using the Number application setting The following code demonstrates this: ' VB If ConfigurationManager.AppSettings("Title") IsNot Nothing Then Me.Title = ConfigurationManager.AppSettings("Title") titleTextBox.Text = ConfigurationManager.AppSettings("Title") End If Lesson 1: Configuring Applications 375 If ConfigurationManager.AppSettings("Number") IsNot Nothing Then numberSlider.Value = _ Double.Parse(ConfigurationManager.AppSettings("Number")) End If // C# if (ConfigurationManager.AppSettings["Title"] != null) { this.Title = ConfigurationManager.AppSettings["Title"]; titleTextBox.Text = ConfigurationManager.AppSettings["Title"]; } if (ConfigurationManager.AppSettings["Number"] != null) numberSlider.Value = double.Parse(ConfigurationManager.AppSettings["Number"]); Build the application, and then manually run the executable file You cannot run the application from the debugger and have the settings correctly stored in the config file With the application running, type text into the Title box Then, move the Number slider Click Save Settings and then close the application Examine the RememberSettings.config file, which is located in the same folder as the executable file Notice that two settings are defined in the section: Title and Number Edit the values of both and save the RememberSettings.config file 10 Rerun the executable file Notice that the application automatically reads the settings that you defined and restores both the window title and the number value Lesson Summary Use the System.Configuration namespace to read and write application settings To write settings, create a Configuration object by calling ConfigurationManager OpenExeConfiguration Then call Configuration.Add to add the name and value pair to the application configuration settings Finally, call Configuration.Save to write the updated values to the configuration file To read settings, use the static ConfigurationManager.AppSettings collection To access connection strings, use the ConfigurationManager.ConnectionStrings static collection To read systemwide configuration settings in the Machine.config file, create a Configuration object by calling the ConfigurationManager.OpenMachineConfiguration method To allow you to access custom application configuration settings using strong types, derive a custom class from ConfigurationSection Although it’s deprecated in NET Framework version 2.0 and later, you can also implement the IConfigurationSectionHandler interface 376 Chapter Installing and Configuring Applications Lesson Review You can use the following questions to test your knowledge of the information in Lesson 1, “Configuring Applications.” The questions are also available on the companion CD if you prefer to review them in electronic form NOTE Answers Answers to these questions and explanations of why each answer choice is right or wrong are located in the “Answers” section at the end of the book Systems administrators have configured a connection string in the Machine.config file on every computer It is identified using the key MySql, and there are no other connection strings How can you access the connection string value programmatically? A ' VB ConfigurationManager.AppSettings("MySql") // C# ConfigurationManager.AppSettings["MySql"] B ' VB ConfigurationManager.ConnectionStrings("MySql").ConnectionString // C# ConfigurationManager.ConnectionStrings["MySql"].ConnectionString C ' VB ConfigurationManager.ConnectionStrings.ElementInformation.Source // C# ConfigurationManager.ConnectionStrings.ElementInformation.Source D ' VB ConfigurationManager.AppSettings.GetKey("MySql") // C# ConfigurationManager.AppSettings.GetKey("MySql") You are creating a WPF application and want to read settings from a custom section in the application’s config file Which of the following should you do? (Choose all that apply.) A Create a class that derives from ConfigurationSection B Create a class that implements IConfigurationSectionHandler Lesson 1: Configuring Applications 377 C Define a section in the application’s config file D Create a custom section within the section in the application’s config file You write the following code to store application configuration settings: ' VB ConfigurationManager.AppSettings.[Set]("Key1", "Value1") Dim config As Configuration = _ ConfigurationManager.OpenExeConfiguration( _ ConfigurationUserLevel.None) config.AppSettings.Settings.Add("Key2", "Value2") config.Save((ConfigurationSaveMode.Modified)) config.AppSettings.Settings.Add("Key3", "Value3") // C# ConfigurationManager.AppSettings.Set("Key1", "Value1"); Configuration config = ConfigurationManager.OpenExeConfiguration( ConfigurationUserLevel.None); config.AppSettings.Settings.Add("Key2", "Value2"); config.Save((ConfigurationSaveMode.Modified)); config.AppSettings.Settings.Add("Key3", "Value3"); Which setting is stored? A Key1 B Key2 C Key3 D None of the above ... service processes, threading, and application domains in a NET Framework application? ?? exam objective, complete the following tasks Develop Multithreaded NET Framework Applications For this task, you... processes The best example of application domains in use today is the Microsoft Internet Information Services (IIS) ASP .NET worker process, implemented by w3wp.exe If you have 10 ASP .NET applications... how an assembly can host application domains 318 Chapter Application Domains and Services NET Framework runtime Application domain Assembly Assembly Application domain Application domain Figure

Ngày đăng: 12/08/2014, 20:22

Từ khóa liên quan

Mục lục

  • Chapter 7: Threading

    • Lesson 2: Managing Threads

      • Waiting for Threads to Complete

      • Lab: Manage Threads

      • Lesson Summary

      • Lesson Review

      • Chapter Review

      • Chapter Summary

      • Key Terms

      • Case Scenarios

        • Case Scenario 1: Print in the Background

        • Questions

        • Case Scenario 2: Ensuring Integrity in a Financial Application

        • Questions

        • Suggested Practices

          • Develop Multithreaded .NET Framework Applications

          • Take a Practice Test

          • Chapter 8: Application Domains and Services

            • Before You Begin

            • Lesson 1: Creating Application Domains

              • What Is an Application Domain?

              • The AppDomain Class

              • How to Create an Application Domain

              • How to Load Assemblies in an Application Domain

              • How to Unload an Application Domain

              • Lab: Creating Domains and Loading Assemblies

Tài liệu cùng người dùng

Tài liệu liên quan