12.07.2015 Views

Discover Enterprise Library - Willy .Net

Discover Enterprise Library - Willy .Net

Discover Enterprise Library - Willy .Net

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

Hands-On LabLab Manual<strong>Discover</strong> <strong>Enterprise</strong> <strong>Library</strong>Please do not remove this manual from the labThe lab manual will be available from Comm<strong>Net</strong>


Information in this document is subject to change without notice. The example companies, organizations,products, people, and events depicted herein are fictitious. No association with any real company, organization,product, person or event is intended or should be inferred. Complying with all applicable copyright laws is theresponsibility of the user. Without limiting the rights under copyright, no part of this document may be reproduced,stored in or introduced into a retrieval system, or transmitted in any form or by any means (electronic, mechanical,photocopying, recording, or otherwise), or for any purpose, without the express written permission of MicrosoftCorporation.Microsoft may have patents, patent applications, trademarked, copyrights, or other intellectual property rightscovering subject matter in this document. Except as expressly provided in any written license agreement fromMicrosoft, the furnishing of this document does not give you any license to these patents, trademarks, copyrights,or other intellectual property.©2005 Microsoft Corporation. All rights reserved.Microsoft, MS-DOS, MS, Windows, Windows NT, MSDN, Active Directory, BizTalk, SQL Server, SharePoint,Outlook, PowerPoint, FrontPage, Visual Basic, Visual C++, Visual J++, Visual InterDev, Visual SourceSafe,Visual C#, Visual J#, and Visual Studio are either registered trademarks or trademarks of Microsoft Corporationin the U.S.A. and/or other countries.Other product and company names herein may be the trademarks of their respective owners.Page i


ContentsLAB 1 C# CONFIGURATION BLOCK ......................................................................................................................1Lab Objective .........................................................................................................................................................1Exercise 1: Writing custom settings to a configuration file....................................................................................1Exercise 2: Reading custom settings from a configuration file ...........................................................................10Exercise 3: Configuration file watcher.................................................................................................................11Lab Summary .......................................................................................................................................................12LAB 2 C# DATA ACCESS BLOCK.........................................................................................................................13Lab Objective .......................................................................................................................................................13Exercise 1: Dynamic SQL with the Data Access Block.......................................................................................13Exercise 2: Stored Procedures and Updates with the Data Access Block .........................................................20Exercise 3: Encrypting Connection Information ..................................................................................................23Lab Summary .......................................................................................................................................................27LAB 3 C# CACHING BLOCK ..................................................................................................................................28Lab Objective .......................................................................................................................................................28Exercise 1: Using the Caching Block for performance........................................................................................28Exercise 2: Persistent Caching and Encryption ..................................................................................................33Lab Summary .......................................................................................................................................................38LAB 4 C# ENTERPRISE LIBRARY LOGGING HANDS ON LAB..........................................................................39Lab Objective .......................................................................................................................................................39Exercise 1: Add Logging to an Application..........................................................................................................39Exercise 2: Create and use a custom log sink....................................................................................................48Exercise 3: Create and use a custom log formatter............................................................................................54Lab Summary .......................................................................................................................................................57LAB 5 C# EXCEPTION HANDLING BLOCK..........................................................................................................58Lab Objective .......................................................................................................................................................58Exercise 1: Logging Exceptions ..........................................................................................................................58Exercise 2: Exception Handling Strategies .........................................................................................................69Lab Summary .......................................................................................................................................................75LAB 6 C# CRYPTOGRAPHY BLOCK ....................................................................................................................76Lab Objective .......................................................................................................................................................76Exercise 1: Encrypt and Decrypt secrets ............................................................................................................76Exercise 2: Use a HashProvider to store a one-way hashed password .............................................................84Lab Summary .......................................................................................................................................................92LAB 7 C# ENTERPRISE LIBRARY SECURITY HANDS ON LAB ........................................................................93Lab Objective .......................................................................................................................................................93Exercise 1: Secure an Application ......................................................................................................................93Exercise 2: Use Task Based Authorization In an Application ...........................................................................106Exercise 3: Application Personalization ............................................................................................................111Lab Summary .....................................................................................................................................................115LAB 8 C# BUILD YOUR OWN BLOCK.................................................................................................................116Lab Objective .....................................................................................................................................................116Exercise 1: Review Current Application ............................................................................................................116Page ii


Exercise 2: Create a Notification Block.............................................................................................................117Exercise 3: Add strongly typed configuration data............................................................................................132Exercise 4: Add external providers ...................................................................................................................136Lab Summary .....................................................................................................................................................138LAB 1 VB.NET CONFIGURATION BLOCK..........................................................................................................139Lab Objective .....................................................................................................................................................139Exercise 1: Writing custom settings to a configuration file................................................................................139Exercise 2: Reading custom settings from a Configuration File .......................................................................149Exercise 3: Configuration File Watcher.............................................................................................................150Lab Summary .....................................................................................................................................................151LAB 2 VB.NET DATA ACCESS BLOCK ..............................................................................................................152Lab Objective .....................................................................................................................................................152Exercise 1: Dynamic SQL with the Data Access Block.....................................................................................152Exercise 2: Stored Procedures and Updates with the Data Access Block .......................................................160Exercise 3: Encrypting Connection Information ................................................................................................162Lab Summary .....................................................................................................................................................166LAB 3 VB.NET CACHING BLOCK .......................................................................................................................167Lab Objective .....................................................................................................................................................167Exercise 1: Using the Caching Block for performance......................................................................................167Exercise 2: Persistent Caching and Encryption ................................................................................................173Lab Summary .....................................................................................................................................................177LAB 4 VB.NET ENTERPRISE LIBRARY LOGGING HANDS ON LAB ...............................................................178Lab Objective .....................................................................................................................................................178Exercise 1: Add Logging to an Application........................................................................................................178Exercise 2: Create and use a custom log sink..................................................................................................188Exercise 3: Create and use a custom log formatter..........................................................................................194Lab Summary .....................................................................................................................................................198LAB 5 VB.NET EXCEPTION HANDLING BLOCK ...............................................................................................199Lab Objective .....................................................................................................................................................199Exercise 1: Logging Exceptions ........................................................................................................................199Exercise 2: Exception Handling Strategies .......................................................................................................211Lab Summary .....................................................................................................................................................217LAB 6 VB.NET CRYPTOGRAPHY BLOCK..........................................................................................................218Lab Objective .....................................................................................................................................................218Exercise 1: Encrypt and Decrypt Secrets..........................................................................................................218Exercise 2: Use a HashProvider to store a one-way hashed password ...........................................................226Lab Summary .....................................................................................................................................................234LAB 7 VB.NET ENTERPRISE LIBRARY SECURITY HANDS ON LAB..............................................................235Lab Objective .....................................................................................................................................................235Exercise 1: Secure an Application ....................................................................................................................235Exercise 2: Use Task Based Authorization In an Application ...........................................................................248Exercise 3: Application Personalization ............................................................................................................254Lab Summary .....................................................................................................................................................258LAB 8 VB.NET BUILD YOUR OWN BLOCK........................................................................................................259Page iii


Lab Objective .....................................................................................................................................................259Exercise 1: Review Current Application ............................................................................................................259Exercise 2: Create a Notification Block.............................................................................................................260Exercise 3: Add Strongly Typed Configuration Data.........................................................................................275Exercise 4: Add External Providers...................................................................................................................279Lab Summary .....................................................................................................................................................281Page iv


Figure 1.1Never Enough PI• The user interface is configurable. Select the Options | Background Color menu command tochange the background color. Select the Options | Font menu command to change the form font.The number of digits of pi to calculate is also selectable via the NumericUpDown control.Task 2 – Adding a Configuration Data Class• Add the following code to the EnoughPIData class in the EnoughPI.Configuration project.public class EnoughPIData{private int m_digits;private Color m_backColor;private FontData m_font;public EnoughPIData() {}public EnoughPIData(int digits, Color backColor, Font font){this.Digits = digits;this.BackColor = backColor;this.Font = font;}public int Digits{get { return this.m_digits; }set { this.m_digits = value; }}[XmlIgnore()]public Color BackColor{get { return this.m_backColor; }Page 2


}set { this.m_backColor = value; }[XmlElement("BackColor")]public string HtmlBackColor{get { return ColorTranslator.ToHtml(this.m_backColor); }set { this.m_backColor = ColorTranslator.FromHtml(value); }}[XmlIgnore()]public Font Font{get{return new Font(this.FontData.Name, this.FontData.Size,(FontStyle) this.FontData.Style);}set{this.FontData = new FontData();this.FontData.Name = value.Name;this.FontData.Size = value.Size;this.FontData.Style = Convert.ToInt32(value.Style);}}}[XmlElement("Font")]public FontData FontData{get { return this.m_font; }set { this.m_font = value; }}Note: To save the configuration data you create a class to hold the configuration data. You thenuse the Configuration Application Block infrastructure to serialize the data to a configurationfile. Note the classes are XML Serializable (have a default public constructor and public properties)as you will use the XML Serialization infrastructure of the Configuration Application Block.• Select Build | Build Solution to compile the complete solution.Task 3 – Adding a Custom Configuration Section• Add a new Application configuration file (App.config) to the EnoughPI project. Click on theEnoughPI project. Select the File | Add New Item menu command. Select Application configurationfile template. Leave the Name as App.config.Page 3


Figure 1.2Add New Item• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.• Select File | Open Application and browse to the App.configfile located here [C:\Microsoft Hands-On-Lab\HOL-pnp01\Source\cs\exercises\ex01\begin\EnoughPI].• Right click on the Application and select New | Configuration Application Block.Figure 1.3Create a new Configuration Application BlockPage 4


• Right click on the Configuration Application Block and select New | Configuration Section.Figure 1.4Create a new Configuration Application Block• Rename the configuration section as EnoughPISettings.Figure 1.5<strong>Enterprise</strong> <strong>Library</strong> ConfigurationPage 5


• Right click on the EnoughPISettings configuration section and select New | XML File StorageProvider.Figure 1.6Create a new XML File Storage Provider• Enter EnoughPISettings.configin the FileName field of the XML File Storage Provider.Figure 1.7Create a new XML File Storage ProviderPage 6


Note: The configuration settings will be expected to be stored as XML and found in a file namedEnoughPISettings.config in the same directory as the application executable.• Select the File | Save All menu command to save the configuration.• Select the EnoughPI project in Visual Studio. Select the Project | Show All Files menu command.Note an empty EnoughPISettings.configfile has been created for you.• Select the Project | Properties menu command to view the EnoughPI Property Pages. SelectCommon Properties | Build Events and add the following code to the Post-build Event CommandLine.copy "$(ProjectDir)\*.config" "$(TargetDir)"Figure 1.8Note: Unless specified otherwise, the configuration section file is expected to be in the samedirectory as the application. Hence you must copy the configuration section file to the compilationtarget directory.• In the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool, right click on the EnoughPISettings configurationsection and select New | XML Serializer Transformer.Page 7


Figure 1.9Create a new Xml Serializer TransformerNote: The XML Serializer Transformer stands between the application and the XML file storageto serialize and deserialize our custom configuration data.• Select the File | Save All menu command to save the configuration. Close the <strong>Enterprise</strong> <strong>Library</strong>Configuration tool.Task 4 – Saving Custom Configuration Settings• Select the EnoughPI project. Select the Project | Add Reference menu command. Select theBrowse button and select the following assemblies located here [C:\Program Files\Microsoft<strong>Enterprise</strong> <strong>Library</strong>\bin].Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Common.dll,Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Configuration.dll.• Select the MainForm.cs file. Select the View | Code menu command. Add the followingnamespace inclusion to the list of namespaces.using Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Configuration;• Add the following code to the SaveConfiguration method in the MainForm.cs file.private void SaveConfiguration(){// Collect Configuration DataEnoughPIData configData;configData = new EnoughPIData(Convert.ToInt32(this.numericUpDown1.Value),this.panel2.BackColor,Page 8


this.Font);// Save Configuration DataConfigurationManager.WriteConfiguration("EnoughPISettings",configData);}Note: The code collects the custom configuration you would like to save by populating anEnoughPIData object. The ConfigurationManager is then used to write the configuration to the datastore identified in the EnoughPISettings configuration section.• Select the Debug | Start menu command to run the application. Customize the interface bymodifying the background color and font (selected off the Options menu). Select the Configuration |Save menu command to save the configuration.Figure 1.10Never Enough PI• View the configuration settings file (EnoughPISettings.config) in the bin\Debug [C:\Microsoft Hands-On-Lab\HOL-pnp01\Source\cs\exercises\ex01\begin\EnoughPI\bin\Debug] directory. The fileshould look very similar to the XML below (depending on your selected background color and font).456#FFFF80Century Gothic11.252Page 9


Note: Note the file contains XML and the root element matches the configuration section name.The xmlSerializerSection element is used by the XML Serializer Transformer to identify theserialized type.• Close Visual Studio.• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp01\Source\cs\exercises\ex01\end\EnoughPI.sln]Exercise 2: Reading custom settings from a configuration fileIn this exercise you will read custom configuration settings from a configuration file.Task 1 – Loading Custom Configuration Settings• Open the EnoughPI [C:\Microsoft Hands-On-Lab\HOLpnp01\Source\cs\exercises\ex02\begin\EnoughPI.sln]file, and build the solution.• Select the Debug | Start menu command (or press F5) to run the application. Note the defaultbackground color is blue, and the default font is Microsoft Sans Serif, 12pt.• Add the following code to the LoadConfiguration method in the MainForm.cs file.private void LoadConfiguration(){// Read Configuration DataEnoughPIData configData;configData =ConfigurationManager.GetConfiguration("EnoughPISettings") asEnoughPIData;}// Apply Configuration Datathis.numericUpDown1.Value = configData.Digits;this.panel2.BackColor = configData.BackColor;this.Font = configData.Font;this.Refresh();Note: The ConfigurationManager is used to read the configuration settings from the data storeidentified in the EnoughPISettings configuration section. The settings are then applied to the form.• For performance reasons, the Configuration Application Block caches the configuration sections (sothat file access is minimized). It will automatically clear the cache when the file changes (after theconfiguration polling interval).• Run up two copies of the application without debugging (Select the Debug | Start WithoutDebugging menu command or press Ctrl+F5). In one application customize the interface bymodifying the background color and font (selected off the Options menu), and select theConfiguration | Save menu command.In the second application, wait 15 seconds, then select Configuration | Load to reload the settings.Page 10


The second application should now look the same as the first.private void LoadConfiguration(){ConfigurationManager.ClearSingletonSectionCache("EnoughPISettings");// Read Configuration DataEnoughPIData configData;configData =ConfigurationManager.GetConfiguration("EnoughPISettings") asEnoughPIData;}// Apply Configuration Datathis.numericUpDown1.Value = configData.Digits;this.panel2.BackColor = configData.BackColor;this.Font = configData.Font;this.Refresh();Note: Why wait 15 seconds? The Configuration Application Block infrastructure caches theconfiguration file settings and relies on file watcher to inform it when the configuration files change.The watcher works on a polling interval of 15 seconds. Hence if you had not waited 15seconds you may have been reading the old cached settings rather than the new savedsettings. If you had desired to always read the data from the file, and never use the cachedsettings you could clear the cache before getting the configuration data (see the code above).• Close Visual Studio.Exercise 3: Configuration file watcherIn this exercise you will monitor changes to the configuration file and reload the configuration settings ifthe file is modified.Task 1 – Handling the ConfigurationChanged Event• Open the EnoughPI [C:\Microsoft Hands-On-Lab\HOLpnp01\Source\cs\exercises\ex03\begin\EnoughPI.sln]file, and build the solution.• Often you would like an application to monitor configuration settings, and automatically load newsettings. The XML File Storage Provider is watching the configuration file, and theConfgurationManager will notify us when the file is changed by raising an event. Handle theConfigurationChanged event by adding the following code to the MainForm_Load method in theMainForm.cs file.private void MainForm_Load(object sender, System.EventArgs e){this.LoadConfiguration();ConfigurationManager.ConfigurationChanged +=newConfigurationChangedEventHandler(ConfigurationManager_ConfigurationChanged);Page 11


}• Add the following method to the MainForm class.private void ConfigurationManager_ConfigurationChanged(object sender,ConfigurationChangedEventArgs e){this.Invoke(new MethodInvoker(this.LoadConfiguration));}Note: When a change is detected on the configuration file, you simply reload the configurationsettings. Note that the thread that the event is fired on is not the main UI thread, so it is necessaryto use the Invoke method to marshal the call onto the main thread before updating any UI elements.• Run up two copies of the application without debugging (Select the Debug | Start WithoutDebugging menu command or press Ctrl+F5). In one application customize the interface bymodifying the background color and font (selected off the Options menu), and select theConfiguration | Save menu command.After a period of time (up to 15 seconds) the second application will detect the configuration filehas changed and reload the settings.Task 2 – More information• For more information on how to manage configuration in large application development projects,including extending the configuration and handling deployment issues, see the "AdvancedMicrosoft® .NET Application Deployment Techniques" course which is part of the MasteringIndustrial Strength .NET series. This uses <strong>Enterprise</strong> <strong>Library</strong> to support centralized management ofboth user configuration and system configuration data, with auto-updating applications based on theUpdater Application Block v2.• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp01\Source\cs\exercises\ex03\end\EnoughPI.sln]Lab SummaryIn this lab you performed the following exercises.• Writing custom settings to a configuration file• Reading custom settings from a configuration file• Configuration file watcherPage 12


Lab 2 C# Data Access BlockThis lab demonstrates the use of the <strong>Enterprise</strong> <strong>Library</strong> Data Access Block.Lab ObjectiveTime to complete: 30 minutesThe objectives of this lab are: query a database using encrypted configuration settings; use the dataaccess block to read and update a database.• Dynamic SQL with the Data Access Block• Stored Procedures and Updates with the Data Access Block• Encrypting Connection InformationExercise 1: Dynamic SQL with the Data Access BlockThis exercise demonstrates how to do basic database access, using the Data Access Block from the<strong>Enterprise</strong> <strong>Library</strong>. It will also show how to configure the block, providing runtime database selection,accessed via an alias in the code.Task 1 – Create the QuickStarts Database• Open the SimpleData [C:\Microsoft Hands-On-Lab\HOLpnp02\Source\cs\exercises\ex01\begin\SimpleData.sln]file, and build the solution.• As part of the installation of <strong>Enterprise</strong> <strong>Library</strong>, there is a batch file to set up the Quick Startsdatabase. Unfortunately, the deployed .sql file is in ANSI, while OSQL uses the OEM code page.When you run the script, you may find that the data entered into the database has its accentsdistorted. To fix this problem, re-save the file as UTF16, and then re-run the batch file.• Open the file DataAccessQuickStarts.sql in Notepad, which can be found in the [<strong>Enterprise</strong> InstallDir]\QuickStarts\Data [C:\Program Files\Microsoft <strong>Enterprise</strong> <strong>Library</strong>\QuickStarts\Data] directory.Choose File | Save As. Select Unicode as the encoding.Page 13


• Click Save, and click Yes to overwrite the file.Figure 1.1• Run the batch file SetUpQuickStartsDB.bat, from the same directory.Task 2 – Review the Application• Open the MainForm.cs file in the designer. This application contains a DataGrid and some menus,which have no event handlers yet. You will implement counting the available customers in thedatabase, and then loading the customers into the datagrid, using the data access block.Task 3 – Implement the Customer Menu Items• Double click on the Customers | Count menu item in the designer.Note: This will create an empty event handler for the menu item click event. Within this method, youwant to query the database to find out how many customers are available.• Select the CustomerManagment project. Select the Project | Add Reference … menu command.Select the Browse button and select the following assemblies located here [C:\ProgramFiles\Microsoft <strong>Enterprise</strong> <strong>Library</strong>\bin]:Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Data.dll,Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Configuration.dll.Note: You will need to add the reference to the Configuration assembly, because most of theproviders APIs within <strong>Enterprise</strong> <strong>Library</strong> derive from either ConfigurationProvider, for abstractclasses, or IConfigurationProvider, for interfaces, which are both contained within the Configurationassembly.Page 14


• Add the following namespace inclusion to the list of namespaces at the top of the file:Using Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Data;• Add the code below to the Count menu item handler (inserted code in bold):private void mnuCount_Click(object sender, System.EventArgs e){Database db =DatabaseFactory.CreateDatabase("QuickStarts Instance");int count =(int) db.ExecuteScalar(CommandType.Text, "SELECT COUNT(*) FROMCustomers");MessageBox.Show("There are " + count.ToString() + " customers in thedatabase");}Note: The code above first obtains an <strong>Enterprise</strong> <strong>Library</strong> database instance using the data accessconfiguration for the database instance name "QuickStarts Instance" within the configurationfile. The real database connection is not opened at this point.The db.ExecuteScalar command hasmultiple overloads. The selected overload allows specifying some literal SQL to execute, andreturns the result in a similar manner to the SqlCommand .ExecuteScalar method.Thedb.ExecuteScalar method call is responsible for opening and closing the connection to the realdatabase defined in the configuration file, as well as performing any instrumentation required.• Double click on MainForm.cs in the solution explorer to re-open the form in design view.• Double click on the Customers | Load menu item, to add an empty menu click event handler, andinsert the following code:private void mnuLoad_Click(object sender, System.EventArgs e){Database db =DatabaseFactory.CreateDatabase("QuickStarts Instance");DataSet ds =db.ExecuteDataSet(CommandType.Text, "SELECT * From Customers");}dataGrid1.DataSource = ds.Tables[0];Note: The db.ExecuteDataSet method is responsible for opening and closing the connection, aswell as returning a new dataset filled with the results of the SQL Query, which may include multipletables.Task 4 – Configure the application• Add a new Application configuration file (App.config) to the CustomerManagement project. Click onthe CustomerManagement project. Select the File | Add New Item menu command. SelectApplication configuration file template. Leave the Name as App.config.Page 15


Figure 1.2• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.• Select File | Open Application and browse to the App.configfile located here [C:\Microsoft Hands-On-Lab\HOL-pnp02\Source\cs\exercises\ex01\begin\CustomerManagement].• Right click on the Application and select New | Data Access Application Block.Page 16


Figure 1.3New Data Access Application Block• Select the node Application | Data Access Application Block | Connection Strings | SQL ConnectionString | database. Change the Value property on the right hand side to EntLibQuickStarts.Page 17


Figure 1.4Changing the Value PropertyNote: These properties are the components of the connection string that will be used to connect byany database instance that references the connection string node SQL Connection String.• Similarly, select the server node in the tree, and set its Value to (local).• Select the Database Instances | Database Instance node in the tree, and change the Nameproperty to QuickStarts Instance.Page 18


Figure 1.5Changing the Name PropertyNote: This name has to match the name you used in the code. This is how you can create an aliasin code that maps to the concrete database instance you wish to use at runtime.• Save the application configuration by choosing File | Save All. Close the <strong>Enterprise</strong> <strong>Library</strong>Configuration Console.Task 5 – Add the post build step• Switch back to Visual Studio.NET, and notice that the App.config file now contains a reference tothe data access settings you added previously. This references the file dataConfiguration.config,which needs to be automatically copied into the bin directory so that it is accessible at runtime.• Select the Project | Properties menu command to view the CustomerManagement Property Pages.Select Common Properties | Build Events and add the following code to the Post-build EventCommand Line.copy "$(ProjectDir)\*.config" "$(TargetDir)"Page 19


Figure 1.6Post Build Event Command LineNote: Unless specified otherwise, the configuration section file is expected to be in the samedirectory as the application. Hence you must copy the configuration section file to the compilationtarget directory.Task 6 – Run the application• Run the application. Click the Customers | Count menu to view the number of customers in thedatabase, and use the Customers | Load menu to fill the Data Grid.• Close the application.• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp02\Source\cs\exercises\ex01\end\SimpleData.sln]Exercise 2: Stored Procedures and Updates with the Data Access BlockThis exercise demonstrates using the data access block to wrap stored procedure access, as well asperforming updates using a strongly typed DataSet.Task 1 – Add the Categories Table to the QuickStarts Database• Open the DataEx2 [C:\Microsoft Hands-On-Lab\HOLpnp02\Source\cs\exercises\ex02\begin\DataEx2.sln]file, and build the solution.• Run the batch file SetUpEx02.bat, which can be found in the lab directory:labs\cs\Data Access\exercises\ex02\DbSetup [C:\Microsoft Hands-On-Lab\HOLpnp02\Source\cs\exercises\ex02\DbSetup].Page 20


Note: This adds a set of categories that you can use to filter the set of products you retrieve andmanipulate.Task 2 – Review the application• Open the MainForm.csfile in the designer. This application will allow us to select a particularcategory, load the products for that category, and then save any changes you make.Task 3 – Implement database retrieval• Right click over the form, and select View Code.• Add the following using statement to the top of the form. You have already added the requiredreferences to the Data and Configuration assemblies.using Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Data;• Add the following private field to the form, as you'll re-use this database instance in multiple places.private Database _db =DatabaseFactory.CreateDatabase("QuickStarts Instance");Note: Note that you can hold onto this, as the Database instance does not represent an openconnection, but instead represents a reference to a database. If you had a SqlConnection objectinstead, then you would not be complying with the Acquire Late, Release Early model.• Find the MainForm_Load method, and insert the following code to obtain the set of categories usinga DataReader:private void MainForm_Load(object sender, System.EventArgs e){using (IDataReader dr = _db.ExecuteReader("GetCategories")){while (dr.Read()){cmbCategory.Items.Add(new Category(dr.GetInt32(0),dr.GetString(1), dr.GetString(2)));}}}Note: One of the overloads of the Database.ExecuteReader method takes a string, and an optionalset of parameters. This overload will locate the stored procedure given by the string, read its metadata(and cache it for future use), and set parameters with the values of any argumentsprovided.Note: You are not doing any connection management here, but it is very important toDispose the data reader returned. This is accomplished by the using statement in the code above.When the data reader is disposed, the underlying DbConnection is also closed.• Find the cmbCategory_SelectedIndexChanged method and insert the following code, which willread a subset of the products, based on the selected category drop down.private void cmbCategory_SelectedIndexChanged(object sender,System.EventArgs e){dsProducts.Clear();Category selectedCategory = (Category) cmbCategory.SelectedItem;if (selectedCategory == null)return;Page 21


}_db.LoadDataSet("GetProductsByCategory",dsProducts,new string[] {"Products"},selectedCategory.CategoryId);Note: There are two methods on the Database class that populate a DataSet; ExecuteDataSet andLoadDataSet. ExecuteDataSet returns a newly created DataSet, while LoadDataSet populates anexisting one.The overload used here takes a stored procedure as the first argument, the dataset topopulate, a set of tables to map the result of the procedure into, and an arbitrary number ofarguments. The additional arguments are mapped to any stored procedure arguments retrievedfrom the database metadata.Task 4 – Implement updating the database• Find the btnSave_Click method. Insert the following code, which will update the database based onany changes in the dataset.private void btnSave_Click(object sender, System.EventArgs e){DBCommandWrapper insertCommandWrapper =_db.GetStoredProcCommandWrapper("HOLAddProduct");insertCommandWrapper.AddInParameter("@ProductName", DbType.String,"ProductName", DataRowVersion.Current);insertCommandWrapper.AddInParameter("@CategoryID", DbType.Int32,"CategoryID", DataRowVersion.Current);insertCommandWrapper.AddInParameter("@UnitPrice", DbType.Currency,"UnitPrice", DataRowVersion.Current);DBCommandWrapper deleteCommandWrapper =_db.GetStoredProcCommandWrapper("HOLDeleteProduct");deleteCommandWrapper.AddInParameter("@ProductID", DbType.Int32,"ProductID", DataRowVersion.Original);deleteCommandWrapper.AddInParameter("@LastUpdate", DbType.DateTime,"LastUpdate", DataRowVersion.Original);DBCommandWrapper updateCommandWrapper =_db.GetStoredProcCommandWrapper("HOLUpdateProduct");updateCommandWrapper.AddInParameter("@ProductID", DbType.Int32,"ProductID", DataRowVersion.Original);updateCommandWrapper.AddInParameter("@ProductName", DbType.String,"ProductName", DataRowVersion.Current);updateCommandWrapper.AddInParameter("@CategoryID", DbType.Int32,"CategoryID", DataRowVersion.Current);updateCommandWrapper.AddInParameter("@UnitPrice", DbType.Currency,"UnitPrice", DataRowVersion.Current);updateCommandWrapper.AddInParameter("@LastUpdate", DbType.DateTime,"LastUpdate", DataRowVersion.Original);_db.UpdateDataSet(dsProducts, "Products",insertCommandWrapper, updateCommandWrapper,deleteCommandWrapper,UpdateBehavior.Standard);}Page 22


Note: When updating a database, it is required to manually create the wrappers around the storedprocedures, as it needs to know the mapping between the columns in the DataTable and the storedprocedure arguments.With this overload of the UpdateDataSet method, it is possible to get the DataAccess Block to execute all the updates transactionally, by setting the UpdateBehaviour toTransactional. For more information, see the <strong>Enterprise</strong> <strong>Library</strong> documentation.Task 5 – Run the application• The application configuration file and data access configuration file, have been pre-created, exactlythe same as in the previous exercise.Run the application, select a category from the drop down, and observe how it loads and saves thedata.Task 6 – More information• For more information on designing and implementing data access layers, and knowing whether tocreate strongly-typed data access layers (via code generation) versus weakly typed data accesslayer (e.g. the data access block) see the "Developing Microsoft® .NET Applications Using CodeGeneration, UI Process and Abstraction" course which is part of the series. This uses <strong>Enterprise</strong><strong>Library</strong> as one of the options for abstracting data access, as well as implementing code generationto generate transaction aware data access layers.• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp02\Source\cs\exercises\ex02\end\DataEx2.sln]Exercise 3: Encrypting Connection InformationIn this exercise, you will encrypt the configuration to prevent 'connection string discovery' by someonecopying the application configuration file.Task 1 – Encrypt the Database Configuration• Open the DataEx3 [C:\Microsoft Hands-On-Lab\HOLpnp02\Source\cs\exercises\ex03\begin\DataEx3.sln]file, and build the solution.• Make sure any currently open Configuration Console instances are closed.• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.• Select File | Open Application and browse to theApp.config file located here [C:\Microsoft Hands-On-Lab\HOL-pnp02\Source\cs\exercises\ex03\begin\ProductMaintenance].• Right click over the Encryption Settings node, and add a new File Key Algorithm Storage Provider.Page 23


Figure 3.1New File Key Algorithm ProviderNote: The File Key Algorithm storage provider uses an external file to contain the key used toencrypt and de-crypt the configuration information. This potentially allows multiple applications toshare the same key stored with a single file, which may itself be machine or user encrypted.• Select Create a new key algorithm pair. Click Next.Figure 3.2Create New Key Algorithm Pair• Click Select Algorithm. Choose the RijndaelManaged symmetric encryption algorithm. Click Next.Page 24


Figure 3.3Select AlgorithmNote: This selects the algorithm that will be used to encrypt and decrypt the configuration data.• Click on the Generate button, which will create a random key that is valid for the selected algorithm.Click Next.Figure 3.4Generate Random Key• Click Select File. Type in the filename Configuration.key. Click Save.Note: This is the file that will contain the encryption key. Note:This will be saved as an absolutepath name, which will need to be updated if the application is deployed with the key file.• Check the Enable DPAPI checkbox. Select the Machine radio button.Page 25


Figure 3.5Enable DPAPI CheckboxNote: This will encrypt the key file using the Data Protection API, which is built into Windows. Thismeans that the key file can only be read on this machine. If you had selected the User option, thenthe key file would only have been readable on this machine by this user (if roaming profiles are notenabled for the user), or on any machine within the domain for the user (if roaming profiles areenabled).Note: If you want to deploy a key file to another machine or user, and want to use DPAPIencryption on the target machine, then you will need to deploy it un-encrypted, and encrypt it on themachine at install time (or post install).• Click Finish.• Select the dataConfiguration node of the configuration. Change the Encrypt property to True.• Save the configuration by selecting File | Save All.• Notes:Because the configuration references an external key file, this allows the configuration to bedeployed in an encrypted format. At deployment time, the external key file needs to be encryptedusing DPAPI for either the specific machine, or the specific user. This means that the only machineor user dependant modification for the encryption is the single key file, rather than having to reencryptall the configuration. This allows the configuration to be stored centrally in an encryptedform, encrypted with the shared key that all the clients have, but each client's key is itself encryptedwith DPAPI. This does mean that if the key is compromised on one machine, however, then theconfiguration can be tampered with on all the machines.Another issue to be careful about is that the path to the key file has to be an absolute path, as thecurrent build of the configuration run-time will resolve any relative paths relative to the workingdirectory. In the case of the configuration console, the working directory is initially the consoleexecutable directory, whereas for the applications, the working directory is the application directory.Task 2 – Review the encrypted configuration• Open the app.config. Note the new keyAlgorithmStorageProvider element.• Open the dataconfiguration.config. You will notice that it is now a binary, encrypted file.Page 26


Task 3 – Run the application• Run the application and note that it behaves the same as in the previous exercise.Lab SummaryIn this lab you performed the following exercises.• Dynamic SQL with the Data Access Block• Stored Procedures and Updates with the Data Access Block• Encrypting Connection InformationPage 27


Lab 3 C# Caching BlockThis lab demonstrates the use of the <strong>Enterprise</strong> <strong>Library</strong> Caching Block. It requires SQL Serveror MSDE pre-installed.Lab ObjectiveTime to complete: 30 minutesThe objectives of this lab are: add persistent caching to a windows application; use background loadingto populate a cache.• Using the Caching Block for performance• Persistent Caching and EncryptionExercise 1: Using the Caching Block for performanceTask 1 – Populate the QuickStarts database with employee data• Open the CachingEx01 [C:\Microsoft Hands-On-Lab\HOLpnp03\Source\cs\exercises\ex01\begin\CachingEx01.sln]file, and build the solution.• Run the batch file SetCachingHOL.bat, which can be found in the lab directory:labs\cs\Caching\setup [C:\Microsoft Hands-On-Lab\HOL-pnp03\Source\cs\exercises\].Note: This adds a set of employees which you will use to create an employee directory.Task 2 – Review the application• Open the MainForm.cs file in the designer.Note: This application is a browser for the employee contact details stored in the database. As partof the browser the application displays a photograph of the employee.• Right click over the form, and select View Code. Find the method MainForm_Load. The Form usesa class called EmployeeService to obtain the data to display. This in turn uses theEmployeeDataProvider class, as displayed in the picture below:Page 28


Figure 1.1Note: Currently, the EmployeeService just delegates directly to the EmployeeDataProviderclass. You will enhance this class to use the Caching Block and to react appropriately when theapplication is offline.• Right click over the EmployeeDataProvider.cs file, and select View Code. Find the methodGetEmployeePhotoData. Notice that you have added a delay of 1 second into the method, tosimulate slow access to a database.• Run the application. Browse through the employees. Notice that there is a significant delay whilebrowsing through photos, even when you have viewed a photo previously.Task 3 – Implement caching in the EmployeeService class• Select the CustomerManagment project. Select the Project | Add Reference menu command.Select the Browse button and select the following assembly located here [C:\ProgramFiles\Microsoft <strong>Enterprise</strong> <strong>Library</strong>\bin]:Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Caching.dll.• Double click over the EmployeeService.cs file in the Solution Explorer.• Add the following namespace inclusion to the list of namespaces at the top of the file:using Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Caching;• Replace the code in GetEmployeePhoto with the code below (modified code in bold):public Bitmap GetEmployeePhoto(Guid employeeId){CacheManager cache = CacheFactory.GetCacheManager();byte[] photoData = (byte[]) cache[employeeId.ToString()];if (photoData == null){EmployeeDataProvider dataProvider = new EmployeeDataProvider();photoData = dataProvider.GetEmployeePhotoData(employeeId);}cache.Add(employeeId.ToString(), photoData);if (photoData == null)return null;Page 29


}return new Bitmap(new MemoryStream(photoData));Note: This method uses the factory model, as with the rest of <strong>Enterprise</strong> <strong>Library</strong>, to create a newCacheManager instance. This can be purely in-memory, or can be backed by a physical storagemedium, depending on configuration.Items can be retrieved from the cache by using an indexer,and can be added (or replaced) by using the Add method. The overload used in this method doesnot specify an expiration policy.• Add a new method to the EmployeeService class, to allow the form to request the service to getnew data:public static void ClearCache(){CacheManager cache = CacheFactory.GetCacheManager();cache.Flush();}Note: This method will remove all items from the cache.• Double click on MainForm.cs in the solution explorer to re-open the form in design view.• Double click on the File | Clear Cache menu item, to add an empty menu click event handler, andinsert the following code:private void mnuClearCache_Click(object sender, System.EventArgs e){EmployeeService.ClearCache();}Task 4 – Configure the application• In order to configure the caching block, you require an application configuration file. There isalready one added to this project, which is used for the connection string for the database. You nowneed to add the caching configuration section to it.• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.• Select File | Open Application and browse to the App.configfile located here [C:\Microsoft Hands-On-Lab\HOL-pnp03\Source\cs\exercises\ex01\begin\EmployeeBrowser].• Right click on the Application and select New | Caching Application Block.Page 30


Figure 1.2Caching Application Block• Select the node Application | Caching Application Block | Cache Managers | Cache Manager. Heresome of the settings you can change to tune the performance of our cache. For now, leave thesettings as they are.Page 31


Figure 1.3Caching Application Block• Save the application configuration by choosing File | Save All. Close the <strong>Enterprise</strong> <strong>Library</strong>Configuration Console.Task 5 – Add the post build step• Switch back to Visual Studio.NET, and open the App.configfile. Notice that the App.config file nowcontains a reference to the caching configuration settings you added previously. This references thefile cachingConfiguration.config, which needs to be automatically copied into the bin directory sothat it is accessible at runtime.• Select the Project | Properties menu command to view the EmployeeBrowser Property Pages.Select Common Properties | Build Events and add the following code to the Post-build EventCommand Line.copy "$(ProjectDir)\*.config" "$(TargetDir)"Page 32


Figure 1.4Post-build Event Command LineNote: Unless specified otherwise, the configuration section file is expected to be in the samedirectory as the application. Hence you must copy the configuration section file to the compilationtarget directory.Task 6 – Run the application• Run the application. As you browse through notice the performance difference for cached photos.• Close the application and Close Visual Studio .NET.• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp03\Source\cs\exercises\ex01\end\CachingEx01.sln]Exercise 2: Persistent Caching and EncryptionThis exercise demonstrates using expiration policies, persistent backing stores, and backgroundloading of an offline cache.Task 1 – Add caching to the initial load of the employee contact details• Open the CachingEx02 [C:\Microsoft Hands-On-Lab\HOLpnp03\Source\cs\exercises\ex02\begin\CachingEx02.sln]file, and build the solution.• Instructions.Task 2 – Implement offline cachingPage 33


• Open the EmployeeServices.cs file. Add the following using statement to the top of the file:using Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Caching.Expirations;• Find the method GetContactDetails, and replace the code with:public EmployeesDataSet GetContactDetails(){CacheManager cache = CacheFactory.GetCacheManager();EmployeesDataSet dsEmployees = (EmployeesDataSet)cache["EmployeeContacts"];if (dsEmployees == null){if (ConnectionManager.IsOnline){dsEmployees = LoadContactDetails();}else{dsEmployees = new EmployeesDataSet();}}}return dsEmployees;Note: This code will only attempt to contact the database when the application is online.• Add a new method (LoadContactDetails) which contains the logic to call the data provider, and addthe returned data to the cache:private EmployeesDataSet LoadContactDetails(){CacheManager cache = CacheFactory.GetCacheManager();EmployeeDataProvider dataProvider = new EmployeeDataProvider();EmployeesDataSet dsEmployees = dataProvider.GetEmployees();// Expire in 2 dayscache.Add("EmployeeContacts",dsEmployees,CacheItemPriority.High,null,new ICacheItemExpiration[] { new AbsoluteTime(newTimeSpan(2,0,0,0)) } );}return dsEmployees;Note: This overload of the Add method allows specifying cache item priority, a cache item removalcallback (which must be serializable for persistent caches), and a set of expiration policies. In thiscase, you do not want to allow our users to keep the employee data on their machines for morethan 2 days without checking in. This helps to reduce the possibility of an employee taking thecontact data to another company, as the caching infrastructure will remove the contents once theyhave expired.• Modify the GetEmployeePhoto method to not attempt to retrieve information from the databasewhen offline (modified code in bold):public Bitmap GetEmployeePhoto(Guid employeeId){CacheManager cache = CacheFactory.GetCacheManager();byte[] photoData = (byte[]) cache[employeeId.ToString()];if (photoData == null && ConnectionManager.IsOnline)Page 34


{EmployeeDataProvider dataProvider = new EmployeeDataProvider();photoData = dataProvider.GetEmployeePhotoData(employeeId);cache.Add(employeeId.ToString(), photoData);}if (photoData == null)return null;}return new Bitmap(new MemoryStream(photoData));Task 3 – Configure the persistent cache• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.• Select File | Open Application and browse to the App.configfile located here [C:\Microsoft Hands-On-Lab\HOL-pnp03\Source\cs\exercises\ex02\begin\EmployeeBrowser].• Right click over the node Application | Caching Application Block | Cache Managers | CacheManager. Select New -> Isolated Storage.• Change the Partition Name property to be:Figure 2.1Create a new Isolated StoragePage 35


EmployeeBrowserNote: The Partition Name allows multiple caches to share the same Isolated storage location.• Save the configuration by selecting the menu File | Save All. Close the configuration console.Task 4 – Run the application• Swap back to Visual Studio.NET, and run the application, but only browse to a few of theemployees, to load the cache with data.• Close the application, and open the file ConnectionManager.cs. Change the IsOnline property toreturn false, to simulate the application being started offline.static public bool IsOnline{get{return false;}}Note: Normally this class would be responsible for pinging a server to see whether the client isonline or not. See the offline application block for ways to do this.• Re-run the application. This time, you should see that some of the employees do not have theirphoto in the cache. The next task will be to automatically pre-load the cache in the backgroundwhen online.• Close the application. Change the IsOnline property back to true:static public bool IsOnline{get{return true;}}Task 5 – Implement background pre-loading of the cache when online• Open the EmployeeService.cs file. Add the following two methods, which will load the cache in thebackground:private void PopulateCache(){CacheManager cache = CacheFactory.GetCacheManager();EmployeesDataSet dsEmployees = LoadContactDetails();foreach (EmployeesDataSet.EmployeesRow employee indsEmployees.Employees){if (!cache.Contains(employee.EmployeeID.ToString())){EmployeeDataProvider dataProvider = newEmployeeDataProvider();cache.Add(employee.EmployeeID.ToString(),dataProvider.GetEmployeePhotoData(employee.EmployeeID));}}Page 36


}private delegate void PopulateCacheDelegate();public void BeginBackgroundLoad(){if (!ConnectionManager.IsOnline)return;}PopulateCacheDelegate mi = new PopulateCacheDelegate(PopulateCache);mi.BeginInvoke(null, null);Note: The BeginBackgroundLoad method uses a delegate to begin the PopulateCache method ona background thread, handled by the .NET worker thread implementation.The Caching Blockguarantees us thread safety when using the Cache, so it is safe to access from multiple threads atthe same time.The PopulateCache method gets an updated set of employee data from thedatabase, by calling LoadContactDetails, which also stores the new set of data in the cache. It theniterates through all the rows in the Employees table. Note that this is actually not a safe activity ifthe user can add or remove rows in the dataset on another thread (instead it would be better toSelect the set of rows, and then iterate through them).• Right click over the MainForm.csfile, and select View Code. Find the MainForm_Load method, andadd the following line at the end (inserted code in bold):private void MainForm_Load(object sender, System.EventArgs e){EmployeeService service = new EmployeeService();EmployeesDataSet tempDataset = service.GetContactDetails();dsEmployees.Merge(tempDataset);}service.BeginBackgroundLoad();Task 6 – Run the application• Run the application, and wait about 10 seconds. Shutdown the application, and change the IsOnlineproperty within the ConnectionManager.cs file to be false:static public bool IsOnline{get{return false;}}• Start the application again, and this time all the photos should be available while "offline".Task 7 – More information• For more information on how to use caching in building occasionally connected solutions, seethe Mobile Devices course which is part of the Mastering Industrial Strength .NET series.This uses <strong>Enterprise</strong> <strong>Library</strong> to support caching of reference data and Web service requests, usingboth intrusive application code changes and proxy interception based offline handling techniques.Page 37


• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp03\Source\cs\exercises\ex02\end\CachingEx02.sln]Lab SummaryIn this lab you performed the following exercises.• Using the Caching Block for performance• Persistent Caching and EncryptionPage 38


Lab 4 C# <strong>Enterprise</strong> <strong>Library</strong> Logging Hands On LabThis lab demonstrates the use of the <strong>Enterprise</strong> <strong>Library</strong> Logging and Instrumentation Application Block.Lab ObjectiveTime to complete: 30 minutesThe objectives of this lab are: use the <strong>Enterprise</strong> <strong>Library</strong> Logging and Instrumentation Application Blockto implement logging in an application; configure log sinks and use custom log sinks.• Add Logging to an Application• Create and use a custom log sink• Create and use a custom log formatterExercise 1: Add Logging to an ApplicationIn this exercise you will add logging and tracing to an existing application. You will configure log sinksvia the <strong>Enterprise</strong> <strong>Library</strong> Configuration Tool.Task 1 – <strong>Enterprise</strong> <strong>Library</strong> Configuration Tool• Open the EnoughPI [C:\Microsoft Hands-On-Lab\HOLpnp04\Source\cs\exercises\ex01\begin\EnoughPI.sln]file, and build the solution.• Select the Debug | Start menu command to run the application.The EnoughPI application calculates the digits of pi (p, the ratio of the circumference of a circle toits diameter). Enter your desired precision via the NumericUpDown control and click the Calculatebutton. Be prepared to wait if you want more than 500 digits of precision.Use the <strong>Enterprise</strong> <strong>Library</strong> Logging Application Block to log application state.Page 39


Figure 1.1Never Enough PI• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.• Select File | Open Application and browse to the App.configfile located here [C:\Microsoft Hands-On-Lab\HOL-pnp04\Source\cs\exercises\ex01\begin\EnoughPI].• Right click on the Application and select New | Logging and Instrumentation Application Block.Figure 1.2Create a new Logging and Instrumentation Application BlockPage 40


Note: A Logging and Instrumentation Application Block section is created to set the loggingconfiguration information. A Configuration Application Blocksection is created to set the loggingconfiguration file information.• The default Logging and Instrumentation Application Block Client Settings define an in-processdistribution strategy and specify that logging is enabled.Figure 1.3Create a new Category• The default Logging and Instrumentation Application Block Distributor Settings define twoCategories (General, and Trace). Categories are text tags that you may apply to your log events togroup them. The General category defines one destination, Event Log Destination, which pairs theEvent Log Sink with the Text Formatter. The Trace category defines on destination, Flat FileDestination, which pairs the Flat File Sink with the Text Formatter.New categories may be added by right clicking on Categories and selecting New | Category. Acategory may have many destinations. A destination defines a log formatter and log sink pair.Each formatter and sink many be used in many destinations.Page 41


Figure 1.4Create a new Category• Select the File | Save All menu command to save the configuration. Close the console.• Select the EnoughPI project in Visual Studio. Select the Project | Show All Files menu command.Note two logging configuration files (loggingconfiguration.config andloggingdistributorconfiguration.config) have been created.Note: The loggingconfiguration.config file contains the Client Settings (incl. the DistributionStrategies). The loggingdistributorconfiguration.config file contains the Distributor Settings (incl. theCategories, sinks and Formatters).• The logging configuration files must be copied to the target compilation directory. Select the Project| Properties … menu command to view the EnoughPI Property Pages. Select Common Properties |Build Events and add the following code to the Post-build Event Command Line.copy "$(ProjectDir)\*.config" "$(TargetDir)"Page 42


Figure 1.5EnoughPI Property PagesNote: Unless specified otherwise, the configuration section file is expected to be in the samedirectory as the application. Hence you must copy the configuration section file to the compilationtarget directoryTask 2 – Adding Logging• In Visual Studio select the EnoughPI project. Select the Project | Add Reference … menucommand. Select the Browse button and select the following assembly located here [C:\ProgramFiles\Microsoft <strong>Enterprise</strong> <strong>Library</strong>\bin].Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Logging.dll(Contains the logging application block base classes).• In Visual Studio, open the Calculator.cs file in the Calc folder of the EnoughPI project, and add thefollowing namespace.using Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Logging;• Log the calculation completion by adding the following code to the OnCalculated method in theCalculator.csfile. Create a new LogEntry. Set the desired log entry parameters, and use theLogger to write the log entry to the log Distributor (to send formatted log entries to the categorydestination).protected void OnCalculated(CalculatedEventArgs args){// TODO: Log final resultLogEntry log = new LogEntry();log.Message = string.Format("Calculated PI to {0} digits",args.Digits);log.Category = Category.General;log.Priority = Priority.Normal;Page 43


Logger.Write(log);}if (Calculated != null)Calculated(this, args);Note: Notes: You have used constants for the Category and Priority rather than use hard-codedtags and integers(defined in Constants.cs in the EnoughPI.Logging project).• Log the calculation progress by adding the following code to the OnCalculating method in theCalculator.cs file.protected void OnCalculating(CalculatingEventArgs args){// TODO: Log progressLogger.Write(string.Format("Calculating next 9 digits from {0}",args.StartingAt),Category.General,Priority.Low);if (Calculating != null)Calculating(this, args);if (args.Cancel == true){// TODO: Log cancellationLogger.Write("Calculation cancelled by user!", Category.General,Priority.High);}}Note: Notes: You have used an overload of the Write method on the Logger class to shortcutcreating a LogEntry.• Log calculation exceptions by adding the following code to the OnCalculatorException method inthe Calculator.cs file.protected void OnCalculatorException(CalculatorExceptionEventArgs args){// TODO: Log exceptionif (!(args.Exception is ConfigurationException)){Logger.Write(args.Exception, Category.General, Priority.High);}}if (CalculatorException != null)CalculatorException(this, args);Note: Notes: You must test that the exception type is not a ConfigurationException as you wouldnot be able to use the Logger if it has not be correctly configured.You would normally use the<strong>Enterprise</strong> <strong>Library</strong> Exception Handling Application Block to create a consistent strategy forprocessing exceptions.• Select the Debug | Start menu command to run the application. Enter your desired precision andclick the Calculate button. Log entries will be written to the windows event log.Page 44


• Run the Event Viewer. From the Windows Start menu select Administrative Tools l Event Viewer.View the Application log for messages from the <strong>Enterprise</strong> <strong>Library</strong> Logging source.Figure 1.6Event Viewer• Double click on a log entry to view the formatted log message.Figure 1.7Event PropertiesTask 3 – Adding TracingPage 45


• Often you would like to time sections of our application. The Logging and InstrumentationApplication Block includes tracing which allows us to book-end a section of code and log theexecution time.• Return to the <strong>Enterprise</strong> <strong>Library</strong> Configuration Tool. If you closed it, re-open by selecting WindowsStart menu, select All Programs | Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong><strong>Library</strong> Configuration, and re-open the App.configfile located here [C:\Microsoft Hands-On-Lab\HOL-pnp04\Source\cs\exercises\ex01\begin\EnoughPI].• Set TracingEnabled to True in the Client Settings of the Logging and Instrumentation ApplicationBlock configuration section.Figure 1.8<strong>Enterprise</strong> <strong>Library</strong> Configuration• Select the File | Save All menu command to save the configuration.• In Visual Studio, open the Calculator.cs file in the EnoughPI project, and add thefollowing namespace.using Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Logging.Tracing;• Trace (for timing) the complete calculation by adding the following code to the Calculate method inthe Calculator.csfile of the EnoughPI project.public string Calculate(int digits){StringBuilder pi = new StringBuilder("3", digits + 2);string result = null;Page 46


1);try{if (digits > 0){// TODO: Add Tracing around the calculationusing (new Tracer(Category.Trace, "Calculate PI")){pi.Append(".");for (int i = 0; i < digits; i += 9){CalculatingEventArgs args;args = new CalculatingEventArgs(pi.ToString(), i +OnCalculating(args);// Break out if cancelledif (args.Cancel == true) break;}}}// Calculate next 9 digitsint nineDigits = NineDigitsOfPi.StartingAt(i+1);int digitCount = Math.Min(digits - i, 9);string ds = string.Format("{0:D9}", nineDigits);pi.Append(ds.Substring(0, digitCount));result = pi.ToString();// Tell the world I've finished!OnCalculated(new CalculatedEventArgs(result));}catch (Exception ex){// Tell the world I've crashed!OnCalculatorException(new CalculatorExceptionEventArgs(ex));}}return result;Note: Why enclose the Tracer in a using block? The Tracer will stop timing, and log its end tracemessage, when it is disposed. The using block guarantees us that Dispose() will be called on theTracer at the end of the block. Allowing the Garbage Collector to dispose of the Tracer will result inincorrect timings.• Run the application, and perform a calculation. Close the application.• The Trace has been configured to write to the trace.log [C:\Microsoft Hands-On-Lab\HOLpnp04\Source\cs\exercises\ex01\begin\EnoughPI\bin\Debug\trace.log]file via the Flat File Sink.View the elapsed time in the end trace message.• Close Visual Studio.• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp04\Source\cs\exercises\ex01\end\EnoughPI.sln]Page 47


Exercise 2: Create and use a custom log sinkTask 1 – Create a custom log sink• Open the EnoughPI [C:\Microsoft Hands-On-Lab\HOLpnp04\Source\cs\exercises\ex02\begin\EnoughPI.sln]file, and build the solution.• In Visual Studio select the EnoughPI.Logging project. Select the Project | Add Reference … menucommand. Select the Browse button and select the following assemblies located here [C:\ProgramFiles\Microsoft <strong>Enterprise</strong> <strong>Library</strong>\bin].Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Configuration.dll(All Blocks depend on the Configuration Application Block for handling configuration files etc. Youmust include this assembly for the configuration of our custom sink).Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Logging.dll(Contains the logging application block base classes).• In Visual Studio, open the ConsoleSink.cs file in the Sinksdirectory of the EnoughPI.Loggingproject. Add the following namespaces.using Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Configuration;using Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Logging;using Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Logging.Sinks;usingMicrosoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Logging.Distributor.Configuration;• Add the following code to the ConsoleSink class.public class ConsoleSink: LogSink{private LoggingConfigurationView configView;public override void Initialize(ConfigurationView configurationView){this.configView = configurationView as LoggingConfigurationView;}protected override void SendMessageCore(LogEntry logEntry){CustomSinkData sinkData;sinkData = (CustomSinkData)this.configView.GetSinkData(this.ConfigurationName);// Delimit each log entryConsole.WriteLine((string) sinkData.Attributes["delimiter"]);}}// Write the formatted log entry to the ConsoleConsole.WriteLine(FormatEntry(logEntry));Note: Note: The base class is LogSink, which mandates that you override two abstract methods(Initialize and SendMessageCore). The Initialize method allows us to get access to thePage 48


ConfigurationView (ie. a view of the configuration parameters as specified in the configurationfiles). The SendMessageCore method is invoked by the logging infrastructure to send the messageto the log sink. The ConsoleSink Classis expecting a parameter, named delimiter, be defined in theconfiguration of this custom log sink.• Select Build | Build Solution to compile the complete solution.Task 2 – Using a custom sink• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.• Select File | Open Application and browse to the App.configfile located here [C:\Microsoft Hands-On-Lab\HOL-pnp04\Source\cs\exercises\ex02\begin\EnoughPI].• Right click on the Sinks node of the Logging and Instrumentation Application Block DistributorSettings. Select the New | Custom Sink menu command.Figure 2.1Create a new Custom Sink• Select the Name field and rename the Custom Sink as Console Sink.• Select the TypeName field and click the ellipses to display the Type Selector dialog.Figure 2.2TypeNamePage 49


• Click the Load an Assembly … button and browse to the EnoughPI.Logging.dll assembly here[C:\Microsoft Hands-On-Lab\HOL-pnp04\Source\cs\exercises\ex02\begin\EnoughPI\bin\Debug] andclick the Open button.• Select the ConsoleSink class from the EnoughPI.Logging assembly and click the Ok button.Figure 2.3Type Selector• Select the Attributes field and click the ellipses to display the NameValueItem Collection Editor.Figure 2.4Attributes• Click the Add button to add a new NameValueItem. Set the Name field to delimiter, and the Valueto a line of dashes (eg. ------------------). Click the Ok button.Page 50


Figure 2.5Set NameValueItemNote: Note: You will remember our ConsoleSink is expecting a parameter named delimiter, which isprinted before each log entry is written to the console.• Right click the General category of the Logging and Instrumentation Application Block DistributorSettings. Select the New | Destination menu command.Figure 2.6Create a new Destination• Set the following properties for the destination.Formatter = Text Formatter,Name = Console Destination, andSink = Console Sink.Page 51


• Add a similar destination to the Trace category.Figure 2.7Create a new DestinationFigure 2.8Create a new Destination• Select the File | Save All menu command to save the configuration. Close the ConfigurationConsole.Page 52


Task 3 – View the sink output• In Visual Studio, click on the EnoughPI project. Select the Project | Properties … menu commandto view the EnoughPI Property Pages. Select Common Properties | General and add change theApplication Output Type to Console Application.Figure 2.9EnoughPI Property Pages• In Visual Studio, select the Debug | Start menu command to run the application. Click the Calculatebutton. The log entries will be displayed in the applications Console Window.Page 53


Figure 2.10Never Enough PI• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp04\Source\cs\exercises\ex02\end\EnoughPI.sln]Exercise 3: Create and use a custom log formatterIn this exercise you will add a custom log formatter to a logging application.Task 1 – Create a custom log formatter• Open the EnoughPI [C:\Microsoft Hands-On-Lab\HOLpnp04\Source\cs\exercises\ex03\begin\EnoughPI.sln]file, and build the solution.• In Visual Studio, open the XmlFormatter.cs file in the Formattersdirectory of the EnoughPI.Loggingproject. Add the following namespaces.using Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Configuration;using Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Logging;usingMicrosoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Logging.Distributor.Configuration;using Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Logging.Formatters;• Add the following code to the XmlFormatter class.public class XmlFormatter : ConfigurationProvider, ILogFormatter{private LoggingConfigurationView configView = null;public override void Initialize(ConfigurationView configurationView){this.configView = (LoggingConfigurationView) configurationView;}public string Format(LogEntry log){using (StringWriter sw = newStringWriter(CultureInfo.InvariantCulture)){XmlTextWriter w = new XmlTextWriter(sw);w.Formatting = Formatting.Indented;w.Indentation = 2;w.WriteStartDocument(true);w.WriteStartElement("logEntry");w.WriteAttributeString("Category", log.Category);w.WriteAttributeString("Priority",log.Priority.ToString(CultureInfo.InvariantCulture));w.WriteElementString("Timestamp", log.TimeStampString);w.WriteElementString("Message", log.Message);w.WriteElementString("EventId",log.EventId.ToString(CultureInfo.InvariantCulture));w.WriteElementString("Severity",log.Severity.ToString(CultureInfo.InvariantCulture));w.WriteElementString("Title", log.Title);w.WriteElementString("Machine", log.MachineName);w.WriteElementString("AppDomain", log.AppDomainName);w.WriteElementString("ProcessId", log.ProcessId);w.WriteElementString("ProcessName", log.ProcessName);Page 54


}}}w.WriteElementString("Win32ThreadId", log.Win32ThreadId);w.WriteElementString("ThreadName", log.ManagedThreadName);w.WriteEndElement();w.WriteEndDocument();return sw.ToString();Note: Note: The base class ConfigurationProvider mandates that you override the abstract methodInitialize. The Initialize method allows us to get access to the ConfigurationView (ie. a view of theconfiguration parameters as specified in the configuration files). The ILogFormatter interfacedefines the Format method. You use a XmlTextWriter over a StringWriter to return a stringcontaining XML.• Select Build | Build Solution to compile the complete solution.Task 2 – Use a custom log formatter• Make sure that any previous instance of the <strong>Enterprise</strong> Configuration Console is shut.• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.• Select File | Open Application and browse to the App.configfile located here [C:\Microsoft Hands-On-Lab\HOLpnp04\Source\cs\exercises\ex03\begin\EnoughPI].• Right click on the Formatters node of the Logging and Instrumentation Application Block DistributorSettings. Select the New | Custom Formatter menu command.Figure 3.1Create a new Custom FormatterPage 55


• Select the Name field and rename the Custom Formatter as Xml Formatter.• Select the TypeName field and click the ellipses to display the Type Selector dialog.Figure 3.2TypeName• Click the Load an Assembly … button and browse to the EnoughPI.Logging.dll assembly here[C:\Microsoft Hands-On-Lab\HOL-pnp04\Source\cs\exercises\ex03\begin\EnoughPI\bin\Debug] andclick the Open button.• Select the XmlFormatter class from the EnoughPI.Logging assembly and click the Ok button.Figure 3.3Type Selector• Select the Console Destination for the General category of the Logging and InstrumentationApplication Block Distributor Settings. The Formatter is set to Text Formatter. Set the Formatter toXml Formatter.Similarly set the Formatter for the Console Destination for the Trace category to Xml Formatter.Figure 3.4Formatter• Select the File | Save All menu command to save the configuration.Page 56


• In Visual Studio, select the Debug | Start menu command to run the application. Click the Calculatebutton. The log entries will be displayed in the applications Console Window.Figure 3.5Never Enough PITask 3 – More information• For more information on how and when to instrument applications, including using custom requesttracing to identify issues across multi-tiered applications, see "Using Advanced Microsoft® .NETPerformance and Operational Techniques" course which is part of the Mastering Industrial Strength.NET series. This course shows how to build custom providers for the enterprise logging block andalso extends <strong>Enterprise</strong> <strong>Library</strong> to support request tracing across Web services, with a mechanismthat would inter-operate with non-Microsoft technologies. The course also uses some freelyavailable profiling tools to help solve performance issues once a bottleneck has been identified.• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp04\Source\cs\exercises\ex03\end\EnoughPI.sln]Lab SummaryIn this lab you performed the following exercises.• Add Logging to an Application• Create and use a custom log sink• Create and use a custom log formatterPage 57


Lab 5 C# Exception Handling BlockThis lab demonstrates the use of the <strong>Enterprise</strong> <strong>Library</strong> Exception Handling Block.Lab ObjectiveTime to complete: 30 minutesThe objectives of this lab are: add Exception Logging to an application; use a Replace Handler to hidesensitive information.• Logging Exceptions• Exception Handling StrategiesExercise 1: Logging ExceptionsIn this exercise you will take an application without exception handling, and add local and globalexception handlers that log the exceptions to the Event Log using the Exception ManagementApplication Block.Task 1 – Review the Application• Open the Puzzler [C:\Microsoft Hands-On-Lab\HOLpnp05\Source\cs\exercises\ex01\begin\Puzzler.sln]file, and build the solution.• Open the Puzzler.cs file in the designer. This application performs two functions, it checks thespelling of words against a dictionary (unix dict for size) and it uses the dictionary to generate a listof words that can be constructed from a character list.• There is currently no exception handling in the application. Run the application, and attempt to adda word which contains numbers to the dictionary (type "ab123" in the word to check textbox andclick Add Word).An unhandled exception will occur, which will break into the debugger. Click Continue to allow theapplication to exit and return to Visual Studio.Task 2 – Install <strong>Enterprise</strong> <strong>Library</strong> Instrumentation• Install the enterprise instrumentation by selecting from the Start Menu: Start | All Programs |Microsoft patterns & practices | <strong>Enterprise</strong> <strong>Library</strong> | Install Services.Note: <strong>Enterprise</strong> <strong>Library</strong> has built-in instrumentation that increments performance counters andsends out WMI events. If these performance counters and event types are not registered, then youwill get warning events in your event log. If you do not wish to have the inbuilt instrumentation,remove the defines USEWMI;USEEVENTLOG;USEPERFORMANCECOUNTER from theCommon project, and recompile it. See this post for more information.Page 58


Task 3 – Add Try/Catch Exception Handling• Select the PuzzlerUI project. Select the Project | Add Reference … menu command. Select theBrowse button and select the following assemblies located here [C:\Program Files\Microsoft<strong>Enterprise</strong> <strong>Library</strong>\bin].Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.ExceptionHandling.dll.Note: While this assembly is the only assembly required for the ExceptionHandling API, otherassemblies may need to be available in the bin\debug directory to provide specific exceptionhandling functionality, which you will add later.• Right click over Puzzler.cs in the solution explorer and select View Code. Find thebtnAddWord_Click method, and add a try/catch section around the AddWord and SetError calls.(Inserted code in bold):private void btnAddWord_Click(object sender, System.EventArgs e){try{PuzzlerService.Dictionary.AddWord(txtWordToCheck.Text);errorProvider1.SetError(txtWordToCheck, "");}catch (Exception ex){if(Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.ExceptionHandling.ExceptionPolicy.HandleException(ex, "UI Policy"))throw;MessageBox.Show("Failed to add word " + txtWordToCheck.Text + ",please contact support.");}}Note: NOTE:It is very important to just use the throw statement, rather than throw ex. If you have"throw ex", then the stack trace of the exception will be replaced with a stack trace starting at the rethrowpoint, which is usually not the desired effect.Task 4 – Configure the Application to Use Exception Management• Add a new Application configuration file (App.config) to the PuzzlerUI project. Click on thePuzzlerUI project. Select the File | Add New Item menu command. Select the Applicationconfiguration file template. Leave the Name as App.config.Page 59


Figure 1.1Add New Item• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.• Select File | Open Application and browse to the App.config file located here [C:\Microsoft Hands-On-Lab\HOL-pnp05\Source\cs\exercises\ex01\begin\PuzzlerUI].• Right click on the Application and select New | Exception Handling Application Block.Page 60


Figure 1.2Set Node Name• Add a new exception policy. Change the name of the new policy from Exception Policy to UI Policy.Page 61


Figure 1.3Add Exception PolicyNote: The policy name here must match that specified in the code. Typically you would useconstants to help prevent typographical errors, especially since the exception handling typically isnot tested as thoroughly as normal code paths.• Add the exception type System.Exception to UI Policy.Page 62


Figure 1.4Add Exception TypeNote: This is a filter that specifies which exceptions should be processed by the exception handlingcode, and what handlers to run. In this case, all exceptions derived from System.Exception will beprocessed.• Set the PostHandlingAction for the System.Exception to None.Page 63


Figure 1.5Set the Post Handling ActionNote: This will cause all exceptions to be handled internally within the exception handling code, andthe caller will not be requested to re-throw the exception with its catch block.• Add a Logging Handler for the System.Exception exception type.Page 64


Figure 1.6Add a Logging HandlerNote: Note that this automatically includes the Logging and Instrumentation Application Block inyour configuration.• Set the LogCategory of the Logging Handler to General.• Save the current application in the <strong>Enterprise</strong> <strong>Library</strong> Configuration Console using File | Save All.Note: This will save the changes to your App.Config file as well as adding configuration files to yourproject directory for each block you have utilized.Task 5 – Change the application to include the exception logging assembly• Select the PuzzlerUI project. Select the Project | Add Reference … menu command. Select theBrowse button and select the following assemblies located here [C:\Program Files\Microsoft<strong>Enterprise</strong> <strong>Library</strong>\bin].Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.ExceptionHandling.Logging.dll.Note: Because one of the goals of the <strong>Enterprise</strong> <strong>Library</strong> is to keep the blocks de-coupled, it ispossible to use the Exception Handling block without needing the Logging block (e.g. by creatingyour own exception handler). If you want to use the two blocks together, then you need to use thisassembly, which contains an Exception Handler that logs via the logging block.Page 65


Task 6 – Add a Post-Build step• The configuration settings file is expected to be found in the same directory as the applicationconfiguration file. Therefore you need to copy the exception handling block configuration file to thebin\debug [C:\Microsoft Hands-On-Lab\HOLpnp05\Source\cs\exercises\ex01\begin\PuzzlerUI\bin\Debug]directory before running theapplication. You could copy the file manually, but it would be preferable to automate the copy aspart of the build process.• Right click on the new PuzzlerUI project and select Properties from the popup menu.• In the project properties dialog select Build Events under Common Properties and set the Post-buildevent command line property to the value below.copy "$(ProjectDir)\*.config" "$(TargetDir)" > NulFigure 1.7Select Build Events and Set Event Command Line PropertyNote: Redirecting the output to the Nul device will prevent the message 1 file(s) copied fromappearing in your build output. Note that this will only copy to the debug directory and would needto be changed if you wished to create a release version of the application.• Select the Build | Build Solution menu command. The last thing you will see in the build output isthe message:Performing Post-Build Event...If the post-build event fails for some reason, this will be shown as a build error.Note: Every time you build the application the post build event will run, copying all the config files inthe PuzzlerUI directory to the bin directory.Page 66


Task 7 – Run the Application• Run the Application. Try adding a number (type a number in the Word to check text box, and clickAdd Word) - a MessageBox is displayed with an error - "Failed to add word …, please contactsupport".• Check the Event Log by using the Event Viewer (Control Panel | Administrative Tools | EventViewer). Look at the top of the Application Log. The exception will be logged.Figure 1.8Checking the Event ViewerNote: After the previous exception the Exception Management Block will have written an entry inthe Application Event Log using the Logging Block.• Close the application.Task 8 – Add Global Exception Handling• While it is possible to put try/catch sections around all the event handlers in an application, it isusually better to provide a single generic handler that fires whenever an unhandled exceptionoccurs within the application.• Open Puzzler.cs, and remove the exception handling from the btnAddWord_Click method:private void btnAddWord_Click(object sender, System.EventArgs e){PuzzlerService.Dictionary.AddWord(txtWordToCheck.Text);errorProvider1.SetError(txtWordToCheck, "");}• Open the Startup.cs file.Application execution begins here. There are two events you can use to listen for unhandledexceptions:The Application.ThreadException event is raised when an unhandled exception occurs on thePage 67


thread that is executing the Application.Run method.If an exception is raised during that handler, or occurs on a different thread to the UI, then theAppDomain.UnhandledException event will fire.• Add the following method to the Startup class, which will be used to handle exceptions.public static void HandleException(Exception ex, string policy){Boolean rethrow = false;try{rethrow =Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.ExceptionHandling.ExceptionPolicy.HandleException(ex, policy);}catch (Exception innerEx){string errorMsg = "An unexpected exception occured while callingHandleException with policy '" + policy + "'. ";errorMsg += Environment.NewLine + innerEx.ToString();MessageBox.Show(errorMsg, "Application Error", MessageBoxButtons.OK,MessageBoxIcon.Stop);throw ex;}if (rethrow){throw ex; // WARNING: This will truncate the stack of the exception}else{MessageBox.Show("An unhandled exception occurred and has beenlogged. Please contact support.");}}Note: This method will use the exception handling block, and will also display valid information ifthere is a problem with the exception handling block itself (e.g. missing configuration).It will alsodisplay a message to the user if the exception is swallowed (i.e. not re-thrown).• Add an event handler for the application ThreadException event.public static void Application_ThreadException(object sender,System.Threading.ThreadExceptionEventArgs e){HandleException(e.Exception, "UI Policy");}Note: This event handler will use the policy that you defined before, for the UI layer. In the nextexercise you will customize this policy to allow certain types of exception to "escape" and shut theapplication down.• Add an event handler for the app domain UnhandledException event.public static void AppDomain_UnHandledException(object sender,System.UnhandledExceptionEventArgs e){if (e.ExceptionObject is System.Exception){HandleException((System.Exception)e.ExceptionObject, "UnhandledPolicy");}}Page 68


Note: This handler will use a new policy, named Unhandled Policy, that you will set up in the nextexercise. The Unhandled Policy should almost always just log the exception, and not re-throw.• Connect the event handlers to the events at the begining of the application by including thefollowing code in the Main method (Inserted code in bold).static void Main(){Application.ThreadException += newSystem.Threading.ThreadExceptionEventHandler(Application_ThreadException);AppDomain.CurrentDomain.UnhandledException += newSystem.UnhandledExceptionEventHandler(AppDomain_UnHandledException);Puzzler f = new Puzzler();Application.Run(f);}• Run the Application. Try adding a number (type a number in the Word to check text box and clickAdd Word) - a MessageBox is displayed - "An unhandled exception occurred and has been logged.Please contact support.". Look in the event log for the logged exception.• Close the application.• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp05\Source\cs\exercises\ex01\end\Puzzler.sln]Exercise 2: Exception Handling StrategiesIn this exercise you will secure part of our application service with Code Access Security and then useaTask 1 – View the Service Modifications• Open the Puzzler2 [C:\Microsoft Hands-On-Lab\HOLpnp05\Source\cs\exercises\ex02\begin\Puzzler2.sln]file, and build the solution.• Open DictionaryService.cs in the PuzzlerService project.Note: You added a new class to the PuzzlerService project, named DictionaryService. This classacts as a Service Interface on top of the Dictionary class. Within this class you provide exceptionfiltering and transformation before sending the results back to the client. For techniques to interceptthe call and automatically transform and handle exceptions, see the "Developing Microsoft® .NETService-Oriented Applications" course of Mastering Industrial Strength .NET.Task 2 – Protect the Service's 'Add Word' Functionality with Code Access Security• Open Dictionary.cs in the PuzzlerService project. Find the AddWord method and decorate it with asecurity attribute as follows:[System.Security.Permissions.PrincipalPermission(System.Security.Permissions.SecurityAction.Demand, Role="GrandPoohBah")]public static Boolean AddWord(string wordToAdd)Page 69


Note: This method can now only be executed by a user who is a member of the role GrandPoohBah, an unlikely situation.Note: Decorate the AddWord method in Dictionary.cs notDictionaryService.cs.• Run the application, type a nonsense word (alphabetic - no numbers!) into the 'Word To Check'textbox (ensure that you have an error flashing), then click on the Add Word button. This will call theservice's AddWord function and throw a SecurityException, which you can check in the eventviewer.Figure 2.1Event PropertiesNote: The SecurityException may be serialized from a Server to a Client (over Web Services) andcontains information that may help an attacker break our security. You would prefer to catch and logthe security exception on the server, then pass an exception containing less information to theclient.• Close the application.Task 3 – Configure the application to replace SecurityExceptions• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.• Select File | Open Application and browse to the App.config file located here [C:\Microsoft Hands-On-Lab\HOL-pnp05\Source\cs\exercises\ex02\begin\PuzzlerUI] (in the ex02/begin/PuzzlerUIproject).Note: Note: Do not use any existing Application Configuration that you may already have open -this will be for the previous exercise. Ensure that you Open the App.config for Ex02!• You have already created a policy, named Service Policy. By default, if a policy is empty, thenexceptions will just be re-thrown inside the catch block, so in effect the policy will do nothing.Add a new Exception Type: System.Security.SecurityException to the Service Policy. Change thePostHandlingAction for the SecurityException to ThrowNewException.Page 70


Figure 2.2Add New Exception Type• Add a new Logging Handler to the SecurityException exception type, and change the followingvalues:LogCategory: GeneralTitle: Security Exception in Service LayerPage 71


Figure 2.3Add New Logging Handler• Add a Replace Handler for the SecurityException exception type. Change the ExceptionMessageto "Unauthorized Access", and select System.Security.SecurityException from mscorlib as thereplacement exception type from the type selector dialog.Page 72


Figure 2.4Add a Replace HandlerNote: Although you have kept the type the same, by creating a new SecurityException, this will notprovide the client any of the stack information or internal security exception information, which couldcompromise security.Task 4 – Change the Application to Exit on a Security Exception• To add a new exception type under UI Policy of System.Security.SecurityException. Leave thePostHandlingAction as NotifyRethrow, which will cause the handler for theApplication.ThreadException event to re-throw the exception, and shut down the application.Page 73


Figure 2.5Add New Exception Type• Add a Logging Handler to the SecurityException under UI Policy, and change the property valuesto:Note: LogCategory: GeneralTitle: Security Exception in UI Layer• Save the current application in <strong>Enterprise</strong> <strong>Library</strong> Configuration using File | Save All.Task 5 – Test the Replace Handler• Run the application, type a nonsense (alphabetic) word into the Word To Check textbox (ensurethat you have an error flashing), then click on the Add Word button. If debugging click on Continuewhen it displays an "Unhandled exception" message. Open the event log to look at the eventscreated. You can use the debugger to observe exactly what is happening and relate this to theException Handling Policies.Note: This time the Exception will be shown three times in the Event Log. First a SecurityExceptionis captured in Dictionary.cs. This is caught by the service layer (DictionaryService.cs) which appliesService Policy. This will cause the exception to be written to the event log on the server (with allavailable information included) and then will capture a new replacement SecurityException (withoutspecific stack information). Second the replacing SecurityException is caught by the ApplicationThreadException handler in Startup.cs. This applies UI Policy which will write the exception to theevent log on the client (the same machine in our case) and set the re-throw boolean to true whichallows our code to decide to re-throw the second SecurityException. Third the re-thrownSecurityException is caught by our AppDomain UnhandledException handler (it was thrown fromPage 74


outside the Application.Run) which applies Unhandled Policy. This will log the exception anddisplay a MessageBox informing us that there was an error in the application. The AppDomainUnhandledException handler does not consume exceptions, so the exception continues to pass tothe runtime or debugger exception handler. This will cause a standard unhandled exception dialogbox to be displayed.• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp05\Source\cs\exercises\ex02\end\Puzzler2.sln]Lab SummaryIn this lab you performed the following exercises.• Logging Exceptions• Exception Handling StrategiesPage 75


Lab 6 C# Cryptography BlockThis lab demonstrates the use of the <strong>Enterprise</strong> <strong>Library</strong> Cryptography Block. Keeping secrets is bothimportant and difficult. Probably the most common secrets you wish to keep are connection strings thatoften include usernames and passwords, however the task of securing these is handled for us by theConfiguration Application Block and was discussed in the Data Access topic earlier. This lab looks atsecuring non configuration data and using hashing to secure passwords.Lab ObjectiveTime to complete: 30 minutesThe objectives of this lab are: keep non configuration data secret; store passwords in a secure manner.• Encrypt and Decrypt secrets• Use a HashProvider to store a one-way hashed passwordExercise 1: Encrypt and Decrypt secretsTask 1 – Review the application• Open the Chatter [C:\Microsoft Hands-On-Lab\HOLpnp06\Source\cs\exercises\ex01\begin\Chatter.sln]file, and build the solution.• Open the Chat.cs file in the designer. When you run the application, two Chat forms will be opened,one called "Fred" and one called "Bill". Messages can be passed between these forms. The codebehind the Send and Decrypt buttons is very simple.• Run the application. Type some text in the upper textbox of one of the Chat windows and press theSend button, the message will appear in the second textbox of the other window, Select the Decryptbutton on the second Chat window and the message will be appended to the third, multiline,textbox.Page 76


Figure 1.1DecryptNote: The third window with the "End Chat" button is the Application.Run window, when it closesthe other two windows will close.• Click the "End Chat" button to close the application.• The messages are being sent between the windows as plaintext, you will use the CryptographyBlock to encrypt and decrypt these communications using a symmetric key.Task 2 – Install <strong>Enterprise</strong> <strong>Library</strong> Instrumentation• If you have not already, install the enterprise instrumentation by selecting from the Start Menu: Start| All Programs | Microsoft patterns & practises | <strong>Enterprise</strong> <strong>Library</strong> | Install Services.Note: <strong>Enterprise</strong> <strong>Library</strong> has built in instrumentation that increments performance counters andsends out WMI events. If these performance counters and event types are not registered, then youwill get warning events in your event log. If you do not wish to have the inbuilt instrumentation,remove the defines USEWMI;USEEVENTLOG;USEPERFORMANCECOUNTER from theCommon project, and recompile it. See this post for more information.Task 3 – Change the project to use Encryption• Select the ChatUI project. Select the Project | Add Reference menu command. Select the Browsebutton and select the following assembly located here [C:\Program Files\Microsoft <strong>Enterprise</strong><strong>Library</strong>\bin]:Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Security.Cryptography.dll.• Add the following namespace inclusion to the list of namespaces at the top of the Chat.cs file (youmay have to right click and "View Code"):using Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Security.Cryptography;Page 77


• Add the code below to the Chat class (added code in bold).public class Chat : System.Windows.Forms.Form{... Other Code// String must match the provider name in thesecurityCryptographyConfiguration.config file.private const string symmProvider = "ChatProvider";public Chat Partner;... Other Code}Note: This constant will allow us to map to the appropriate security provider that you will define inour config files.• Modify the code in the btnSend_Click method in the Chat class, changing the it to use theCryptographer class (changed code in bold).private void btnSend_Click(object sender, System.EventArgs e){txtConversation.AppendText(string.Format("{0} >> {1} \r\n", this.Text,txtToSend.Text));Partner.txtToDecrypt.Text =Cryptographer.EncryptSymmetric(symmProvider, txtToSend.Text);}• Modify the code in the btnDecrypt_Click method in the Chat class, changing the it to use theCryptographer class (changed code in bold).private void btnDecrypt_Click(object sender, System.EventArgs e){string plainText = Cryptographer.DecryptSymmetric(symmProvider,txtToDecrypt.Text);txtConversation.AppendText(string.Format("{0} >> {1} \r\n",Partner.Text, plainText));}Task 4 – Configure the application to use Symmetric key Cryptography• Add a new Application configuration file (App.config) to the ChatUI project. Click on the ChatUIproject. Select the File | Add New Item menu command. Select the Application configuration filetemplate. Leave the Name as App.config.Page 78


Figure 1.2Add new Item• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.• Select File | Open Application and browse to the App.configfile located here [C:\Microsoft Hands-On-Lab\HOL-pnp06\Source\cs\exercises\ex01\begin\ChatUI].• Right click on the Application and select New | Cryptography Application Block.Page 79


Figure 1.3Create a new Cryptography Application Block• Add a new symmetric provider. Select RijndaelManaged as the type, click the Generate button toautomatically generate a key, click OK.Page 80


Figure 1.4Create a new Symmetric Algorithm Provider• Change the Provider name to: "ChatProvider" so that it matches the name you put in our codeearlier. Your configuration should look like the image below.Page 81


Figure 1.5<strong>Enterprise</strong> <strong>Library</strong> Configuration• Save the current application in the <strong>Enterprise</strong> <strong>Library</strong> Configuration Console using File | Save All.Note: This will save the changes to your App.Config file as well as adding configuration files to yourproject directory for each block you have utilized.• Close the <strong>Enterprise</strong> <strong>Library</strong> Configuration application.Task 5 – Add the post build step• Switch back to Visual Studio.NET, and notice that the App.config file now contains a reference tothe cryptography configuration settings you added previously. This references the filesecurityCryptographyConfiguration.config, which needs to be automatically copied into the bindirectory so that it is accessible at runtime.• Select the Project | Properties … menu command to view the ChatUI Property Pages. SelectCommon Properties | Build Events and add the following code to the Post-build Event CommandLine.copy "$(ProjectDir)\*.config" "$(TargetDir)" > NulPage 82


Figure 1.6Post-build Event Command LineNote: Unless specified otherwise, the configuration section file is expected to be in the samedirectory as the application. Hence you must copy the configuration section file to the compilationtarget directory.Task 6 – Run the application• Run the application. Type some text in the upper textbox of one of the Chat windows and press theSend button, the message will appear encrypted in the second textbox of the other window, pressthe Decrypt button on the second Chat window and the plaintext of the message will be appendedto the third, multiline, textbox.Figure 1.7DecryptPage 83


Note: The strings being sent between the windows are now encrypted.• You may or may not have noticed that the application has become a little less stable now that youare using Cryptographer (hint, try sending an empty string). you would normally add some code tocheck the strings before you try to encrypt or decrypt them. For example you should check for zerolength strings in both the btnSend_Click and btnDecrypt_Click methods, and check inbtnDecrypt_Click that the strings you are about to decrypt are a multiple of 4 bytes long and onlycontain valid Base64 characters. These checks have been omitted for clarity.• Close the application.Task 7 – Add error handling• The best way to handle exceptions is to make sure that they don't happen in the first place withsome guard code. Lets first make sure that you don't try to encrypt a zero length string. Openchat.cs, view the code and change the btnSend_Click method to look like the following (added codein bold).private void btnSend_Click(object sender, System.EventArgs e){if (txtToSend.Text.Length > 0){txtConversation.AppendText(string.Format("{0} >> {1} \r\n",this.Text, txtToSend.Text));Partner.txtToDecrypt.Text =Cryptographer.EncryptSymmetric(symmProvider, txtToSend.Text);}}• If you type nothing or random characters into the decrypt textbox and click the Decrypt button youwill get an error. You don't need to.• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp06\Source\cs\exercises\ex01\end\Chatter.sln]Exercise 2: Use a HashProvider to store a one-way hashed passwordIn this exercise you will use a one-way hashing algorithm to encrypt a password stored in an XML file.Task 1 – Review the application• Open the Password [C:\Microsoft Hands-On-Lab\HOLpnp06\Source\cs\exercises\ex02\begin\Password.sln]file, and build the solution.• Open the Login.cs file in the designer. This allows us to validate against a username and passwordthat are stored in plaintext in an XML file (user.xml). Open the ChangePassword.cs file. This allowsus to update the user's password.• Open User.cs. This is a class that holds the username and password and knows how to serializefrom and to an xml file (user.xml). Open user.xml - you can see the password in plain text, notwhat you would like.Page 84


Note: It is not necessarily a good idea to encrypt passwords using a symmetric key like the one youused in the previous exercise because if the key is compromised, so are all our passwords.Onewayhashing algorithms are usually used with passwords. You would hash the password beforecomparing it with a hashed password stored somewhere (usually in a database). This way even ifthe database is compromised our passwords are still safe.Adding extra information 'Salt' to thepassword before encrypting it also makes the password much harder to crack. This is the defaultwith the Cryptography Block's hashing providers.• Run the application. This will open the login form, if you enter the correct password (P@ssw0rd)you will see a confirmation MessageBox (username is ignored, use 'Fred' if you like). Click on the"Change Password" button to bring up the Change Password dialog box and change the password,then check user.xml again - you should see the new password.Task 2 – Change the application to use the Hash provider• Select the PasswordUI project. Select the Project | Add Reference … menu command. Select theBrowse button and select the following assembly located here [C:\Program Files\Microsoft<strong>Enterprise</strong> <strong>Library</strong>\bin]:Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Security.Cryptography.dll.• Add the following namespace inclusion to the list of namespaces at the top of the user.cs file:using Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Security.Cryptography;• Add the code below to the User class in the user.cs file (added code in bold). As long as it is insidethe class it doesn't really matter where the code is placed.public class User{... Other Code// String must match the provider name in thesecurityCryptographyConfiguration.config file.private const string hashProvider = "PasswordHasher";... Other Code}Note: This constant will allow us to map to the appropriate security provider as defined in our configfiles.• Add a ChangePassword method to the User class. This will allow us to change the password to anencrypted version.public bool ChangePassword(string oldPassword, string newPassword){_Password = Cryptographer.CreateHash(hashProvider, newPassword);return true;}Note: For the sake of simplicity, you aren't bothering to check the old password.• Add a ValidatePassword method to the User class. This will allow us to check an entered passwordagainst an encrypted one.public bool ValidatePassword(string plainText){return Cryptographer.CompareHash(hashProvider, plainText, _Password);Page 85


}• Click on Login.cs, double click the Login button to go to the btnLogin_Click method and change it tocall our new ValidatePassword method rather than comparing the strings.Replace:if (txtPassword.Text == u.Password)MessageBox.Show("Login Successful");with:if (u.ValidatePassword(txtPassword.Text))MessageBox.Show("Login Successful");• Click on ChangePassword.cs, double click the Change Password button to go to thebtnChangePassword_Click method and change it to call our new ChangePassword method ratherthan changing the password property.Replace:if (txtOldPassword.Text == u.Password){// Go ahead and change the passwordu.Password = txtNewPassword.Text;u.SaveUser();this.Close();}elsewith:if (u.ChangePassword(txtOldPassword.Text, txtNewPassword.Text)){u.SaveUser();this.Close();}elseTask 3 – Configure the application to use a Hash Provider• Add a new Application configuration file (App.config) to the PasswordUI project. Click on thePasswordUI project. Select the File | Add New Item menu command. Select the Applicationconfiguration file template. Leave the Name as App.config.Page 86


Figure 2.1Add New Item• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.• Select File | Open Application and browse to the App.configfile located here [C:\Microsoft Hands-On-Lab\HOL-pnp06\Source\cs\exercises\ex02\begin\PasswordUI].• Right click on the Application and select New | Cryptography Application Block.Page 87


Figure 2.2Create a new Cryptography Application Block• Add a new hash algorithm provider. Select SHA1Managed as the type.Page 88


Figure 2.3Create a new HashAlgorithm Provider• Change the provider name to: "PasswordHasher" to match the name you used in the code earlierand leave SaltEnabled set to True. Your configuration should look like the image below.Page 89


Figure 2.4<strong>Enterprise</strong> <strong>Library</strong> Configuration• Save the current application in the <strong>Enterprise</strong> <strong>Library</strong> Configuration Console using File | Save All.Note: This will save the changes to your App.Config file as well as adding configuration files to yourproject directory for each block you have utilized.• Close the <strong>Enterprise</strong> <strong>Library</strong> Configuration application.Task 4 – Add the post build step• Switch back to Visual Studio.NET, and notice that the App.config file now contains a reference tothe cryptography configuration settings you added previously. This references the filesecurityCryptographyConfiguration.config, which needs to be automatically copied into the bindirectory so that it is accessible at runtime.• Select the Project | Properties menu command to view the PasswordUI Property Pages. SelectCommon Properties | Build Events and add the following code to the Post-build Event CommandLine.copy "$(ProjectDir)\*.config" "$(TargetDir)" > NulPage 90


Figure 2.5Post-build Event Command LineNote: Unless specified otherwise, the configuration section file is expected to be in the samedirectory as the application. Hence you must copy the configuration section file to the compilationtarget directory.Task 5 – Run the application• Run the application. Click on "Change Password" to bring up the Change Password dialog.Note: The stored password is unencrypted, you need to update this to an encrypted versionbefore you try to login.• Enter P@ssw0rd for the old and new passwords, then click the "Change Password" button.Figure 2.6Change PasswordNote: This will call our new ChangePassword method and update the password in the xml file. If thepassword change is successful, the dialog will close.Note that normally we would have set apassword character for these fields so that the passwords would not be displayed. In this case theyhave been left clear so that you can see exactly what is happening.Page 91


• Try to login with any username and the password you just entered. This should work. Otherpasswords should fail.• Close the application and open the user.xml file. You should see an encrypted version of ourpassword in the file - much safer.Task 6 – More information• For an example of using the cryptography block to encrypting security cookies for use with WS-Security Secure Conversation, see the "Using Advanced Microsoft® .NET Application SecurityTechniques" course which is part of the Mastering Industrial Strength .NET series.• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp06\Source\cs\exercises\ex02\end\Password.sln]Lab SummaryIn this lab you performed the following exercises.• Encrypt and Decrypt secrets• Use a HashProvider to store a one-way hashed passwordPage 92


Lab 7 C# <strong>Enterprise</strong> <strong>Library</strong> Security Hands On LabThis lab demonstrates the use of the <strong>Enterprise</strong> <strong>Library</strong>, Security Application Block. Note: This labrequires SQL Server to be installed.Lab ObjectiveTime to complete: 30 minutesThe objectives of this lab are: to use the Security Application Block to add authentication andauthorization to an application; to use the Security Database Administration Console; to use the<strong>Enterprise</strong> <strong>Library</strong> Configuration Tool to configure security providers; to use the Security Applicationblock to add personalization to an application.• Secure an Application• Use Task Based Authorization In an Application• Application PersonalizationExercise 1: Secure an ApplicationIn this exercise you will add authentication and role based authorization to an existing application. Youwill configure application users and roles via the Security Database Administration Console. You willconfigure security providers via the <strong>Enterprise</strong> <strong>Library</strong> Configuration Tool.Task 1 – Creating the Security Database and Populating Users• Open the BugSmak [C:\Microsoft Hands-On-Lab\HOLpnp07\Source\cs\exercises\ex01\begin\BugSmak.sln]file, and build the solution.• Security information (users, passwords, roles etc) will be stored in a SQL Server database. Ourapplication will use the <strong>Enterprise</strong> <strong>Library</strong> QuickStarts database (EntLibQuickStarts) which includesall the tables and stored procedures required. Browse to the QuickStarts\Security [C:\ProgramFiles\Microsoft <strong>Enterprise</strong> <strong>Library</strong>\QuickStarts\Security\] directory (or click here [C:\ProgramFiles\Microsoft <strong>Enterprise</strong> <strong>Library</strong>\QuickStarts\Security\]) then double click on theSetUpQuickStartsDB.batbatch file.• Run the Security Database Console. From the Windows Start menu select All Programs | Microsoftpatterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | Security Database Console.• Select the QuickStarts Database Authentication Provider and click the Connect button.Figure 1.1 Database Authentication ProviderPage 93


• Click the New User button. Enter a new user (user name is Student, password is P@ssw0rd [The 0is a the number zero) and click the Save button.Note: The new user is added to the Users table. The database saved password is a SHA1 saltedhash of the real password.• Click the


Figure 1.3Database Authentication ProviderNote: Student is now a member of the Developer, and Employee roles.• Add another user with the following attributes:Username: BossPassword: P@ssw0rdRoles: Manager, Employee• Close the Security Database Console.Task 2 – Adding Authentication to the Application• In Visual Studio select the BugSmak project. Select the Project | Add Reference menu command.Select the Browse button and select the following assemblies located here [C:\ProgramFiles\Microsoft <strong>Enterprise</strong> <strong>Library</strong>\bin].Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Configuration.dll(All Blocks depend on the Configuration Application Block for handling configuration files. Includethis assembly for the configuration of the AuthenticationProvider).Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Security.dll(Contains the security application block API).• Open the SecurityHelper.cs file in the Helpers directory, and add the following namespace.using Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Security;• Authenticate the user login by adding the following code to the Authenticate method of theSecurityHelper class.public static bool Authenticate(string username, string password){bool authenticated = false;Page 95


TODO: Authenticate CredentialsNamePasswordCredential credentials;credentials = new NamePasswordCredential(username, password);IAuthenticationProvider authprovider;authprovider = AuthenticationFactory.GetAuthenticationProvider();IIdentity identity;authenticated = authprovider.Authenticate(credentials, outidentity);if (!authenticated){throw new SecurityException("Invalid username or password.");}// TODO: Get Roles}return authenticated;Note: Use the AuthenticationFactory to retrieve the default authentication provider. Theauthentication provider will determine if the user credentials are correct, and if so, will create andreturn an identity object representing the user.• This is all the code required to authenticate a user. We now configure the security block to use thedatabase security provider.Task 3 – Setting up a Database Authentication Provider with the <strong>Enterprise</strong> <strong>Library</strong>Configuration Tool• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.• Select File | Open Application and browse to the App.config file located here [C:\Microsoft Hands-On-Lab\HOL-pnp07\Source\cs\exercises\ex01\begin\BugSmak].• Right click on the Application node and select New | Security Application Block.Page 96


Figure 1.4Create a new Security Application BlockNote: A Security Application Blocksection is created to set the security configuration information. AConfiguration Application Blocksection is created to set the security configuration file information.• We will retrieve the authentication information from the database. Right click on the SecurityApplication Block | Authentication node and select the New | Database Authentication Providermenu command.Page 97


Figure 1.5Create a new Database Authentication ProviderNote: A Data Access Application Block section is created to configure the database connection. ACryptography Application Block section is created to configure the authentication password hashfunction.• Select the Data Access Application Block | Connection Strings | SQL Connection String node. Thisnode contains a collection of Name/Value pair properties.Set the value of the database property to EntLibQuickStarts.Set the value of the server property to localhost.The Integrated Security property should be left set to True.• Right click on the Cryptography Application Block | Hash Providers node and select the New | HashAlgorithm Provider menu command.Page 98


Figure 1.6Create a new HashAlgorithm Provider• Select the SHA1Managed type from the Type Selector dialog and click the Ok button.Figure 1.7System Security CryptographyNote: The Security Database Console you used earlier to create the Student user used a saltedSHA1 hash on the password.• Confirm the SaltEnabled property is set to True.Page 99


Figure 1.8Salt Enabled• Select the Security Application Block | Authentication | Database Provider node. Set the Databaseproperty to Database Instance, and the HashProvider property to SHA1Managed.Figure 1.9Create a new HashAlgorithm Provider• Select the Security Application Block node. Set the DefaultAuthenticationInstance to DatabaseProvider.Page 100


Figure 1.10Create a new HashAlgorithm Provider• Select the File | Save All menu command to save the configuration.• Select the BugSmak project in Visual Studio. Select the Project | Show All Files menu command.Note three configuration files (securitycryptographyconfiguration.config,securityconfiguration.config and dataconfiguration.config) have been created.• The configuration files must be copied to the target compilation directory. Select the Project| Properties menu command to view the project Property Pages. Select Common Properties | BuildEvents and add the following code to the Post-build Event Command Line.copy "$(ProjectDir)\*.config" "$(TargetDir)"Page 101


Figure 1.11Note: Unless specified otherwise, the configuration files are expected to be in the same directory asthe application. Hence you must copy the configuration files to the compilation target directoryTask 4 – Adding the Database Provider and Running the Application• In Visual Studio select the BugSmak project. Select the Project | Add Reference menu command.Select the Browse button and select the following assemblies located here [C:\ProgramFiles\Microsoft <strong>Enterprise</strong> <strong>Library</strong>\bin].Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Security.Database.Authentication.dll(Contains the DbAuthenticationProvider class).Note: One of the design goals of the <strong>Enterprise</strong> <strong>Library</strong> is to allow usage of each application blockindependently of the others, but at the same time allow the blocks to be used together. This isaccomplished through using providers which use the other blocks. For example, the securitydatabase authentication provider uses the data access and cryptography blocks. The providersspecified in the configuration file need to be accessible at runtime, which either means installingthem in the Global Assembly Cache, or copying the files into the bin directory (in this caseaccomplished by adding a reference).• Select the Debug | Start menu command to run the application. The sign-in username andpassword (see LoginForm.cs) are preset for the Student user (password is P@ssw0rd). Sign in asStudent to confirm correct setup of the security database and application configuration.Select the File | Sign Out menu command and attempt to sign in with an incorrect user name orpassword.Page 102


Note: Notes: The application is the skeleton of a simple bug tracking system. The application has askeleton for the following functions: Raise a Bug, Assign a Bug to a developer for resolution, andResolve a Bug.The password is case sensitive, but the username is not.• As you exit the application or sign out you may see an unhandled exception dialog box as shownbelow. Please ignore this (click continue).Figure 1.12Microsoft Development EnvironmentNote: This is due to exceptions thrown by existing declarative role-based security(PrincipalPermission Attributes) which will no longer be effective when we implement SecurityApplication Block task authorization later in the lab.• Close the application.Task 5 – Adding Role-Based Authorization• Select the Debug | Start menu command to run the application, and sign in as Student. Select theTasks | Raise Bug menu command. You are confronted with the message "Sorry, you aren'tallowed to access that form". The task forms have PrincipalPermissions defined to limit useraccess to the forms.Currently we have implemented authentication, but have not determined the user roles.TaskRole RequiredRaise BugEmployee, Developer, or ManagerAssign BugManagerResolve BugDeveloper.Note: The RaiseBug form (see RaiseBug.cs file in the TaskForms directory) demands that the userhave either the Developer, Employee, or Manager role. Attempting to run the form without thenecessary authorization results in a SecurityException which is caught by the MainForm (seeMainForm.cs).• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.Page 103


• Select File | Open Application and browse to the App.config file located here [C:\Microsoft Hands-On-Lab\HOL-pnp07\Source\cs\exercises\ex01\begin\BugSmak].• Select the Security Application Block node. Right click on the Roles node and select the New | RoleDatabase Provider menu command.• Set the Database property to Database Instance.Figure 1.13Create a new Role Database ProviderFigure 1.14Database Instance• Select the Security Application Block node. Set the DefaultRolesInstance to RolesDatabaseProvider.Page 104


Figure 1.15Create a new Role Database Provider• Select the File | Save All menu command to save the configuration.• In Visual Studio, open the SecurityHelper.cs file in the Helpers directory, and add the following codeto Authenticate method.public static bool Authenticate(string username, string password){bool authenticated = false;// TODO: Authenticate CredentialsNamePasswordCredential credentials;credentials = new NamePasswordCredential(username, password);IAuthenticationProvider authprovider;authprovider = AuthenticationFactory.GetAuthenticationProvider();IIdentity identity;authenticated = authprovider.Authenticate(credentials, outidentity);if (!authenticated){throw new SecurityException("Invalid username or password.");}// TODO: Get RolesIRolesProvider rolesprovider;rolesprovider = RolesFactory.GetRolesProvider();IPrincipal principal;principal = rolesprovider.GetRoles(identity);// Place user's principal on the threadThread.CurrentPrincipal = principal;}return authenticated;Note: Use the RolesFactory to retrieve the default roles provider. The roles provider will retrievethe roles for the given identity and return a principal which contains the users identity and roles. Touse the authenticated principal in our application we place it on the Thread.Page 105


• Select the Debug | Start menu command to run the application, and sign in as Student. Select theTasks | Raise Bug menu command.Select the other tasks off the Tasks menu.Note: The Student user is a member of the Developer, and Employee roles; and therefore isauthorized to perform the Raise Bug, and Resolve Bug tasks. Student is not a member of theManager role, and is therefore not authorized to perform the Assign Bug task.Sign out, and re-signin as Boss (password P@ssw0rd). You will now be able to perform the Assign Bug task.• Close the Application, the Solution and the <strong>Enterprise</strong> <strong>Library</strong> Configuration app.Note: Be careful which App.Config file you are opening as you work through these exercises. Eachexercise has its own config files and the <strong>Enterprise</strong> <strong>Library</strong> Configuration application will default tothe directory you had open previously (i.e. the previous exercise!).• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp07\Source\cs\exercises\ex01\end\BugSmak.sln]Exercise 2: Use Task Based Authorization In an ApplicationTask 1 – Adding Authorization Rules with the <strong>Enterprise</strong> <strong>Library</strong> Configuration Tool• Open the BugSmak [C:\Microsoft Hands-On-Lab\HOLpnp07\Source\cs\exercises\ex02\begin\BugSmak.sln]file, and build the solution.• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.• Select File | Open Application and browse to the App.config file located here [C:\Microsoft Hands-On-Lab\HOL-pnp07\Source\cs\exercises\ex02\begin\BugSmak].• Right click on the Security Application Block | Authorization node and select the New | AuthorizationRule Provider menu command.Page 106


Figure 2.1Create a new Rule Authorization Provider• Change the name of RuleProvider to BugSmak Rules. Right click on the BugSmak Rules node andselect the New | Rule menu command. The Rule Expression Editor dialog is displayed.Page 107


Figure 2.2Create a new Rule• Add the following Expression, and click the OK button.R:Developer OR R:Employee OR R:ManagerFigure 2.3ExpressionNote: The user must be in either Developer, Employee, or Manager role.• Rename the Rule to Raise Bug.• Add the following Rules and Expressions:Rule NameExpressionRaise Bug **R:Developer OR R:Employee OR R:ManagerAssign BugR:ManagerResolve BugR:Developer OR R:ManagerFigure 2.4Create a new RulePage 108


** Already entered in previous step.Figure 2.5Resolve Bug• Select the Security Application Block node. Set the DefaultAuthorizationInstance to BugSmakRules.Page 109


Figure 2.6Default Authorization Instance• Select the File | Save All menu command to save the configuration.Task 2 – Adding Task Based Authorization• In Visual Studio, select the RaiseBug.cs file in the TaskForms directory. Select the View | Codemenu command. The RaiseBug form no longer uses the hard-coded PrincipalPermissions. Ratherthe more flexible task authorization is checked in the constructor. Each task form has similar codein the constructor.public RaiseBug(){// Test Authorizationif (!SecurityHelper.Authorized(AuthRule.Raise)){throw new SecurityException();}// This call is required by the Windows Form Designer.InitializeComponent();}// TODO: Add any initialization after the InitializeComponent callNote: Notes:For compile-time checking, the rule names (e.g. "Raise Bug") are mapped to constantsin the Constants.csfile.Page 110


• Open the SecurityHelper.cs file in the Helpers directory, and add the following code to Authorizedmethod.public static bool Authorized(string rule){bool authorized = false;// TODO: Task AuthorizeIAuthorizationProvider ruleProvider;ruleProvider = AuthorizationFactory.GetAuthorizationProvider();authorized = ruleProvider.Authorize(Thread.CurrentPrincipal, rule);}return authorized;Note: Use the AuthorizationFactory to retrieve the default authorization provider. The authorizationprovider will test the current principal (roles and identity) against the rule.• Select the Debug | Start menu command to run the application, and sign in as Student. Select theTasks | Raise Bug menu command.Select the other tasks off the Tasks menu.Note: Student (a member of the Developer and Employee roles) is not able to access the AssignBug form as Student is not a member of the Manager role as is required by the Assign Bug rule.• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp07\Source\cs\exercises\ex02\end\BugSmak.sln]Exercise 3: Application PersonalizationIn this exercise you will use the ProfileProvider to implement application personalization.Task 1 – Adding a Profile Database Provider with the <strong>Enterprise</strong> <strong>Library</strong> ConfigurationTool• Open the BugSmak [C:\Microsoft Hands-On-Lab\HOLpnp07\Source\cs\exercises\ex03\begin\BugSmak.sln]file, and build the solution.• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.• Select File | Open Application and browse to the App.config file located here [C:\Microsoft Hands-On-Lab\HOL-pnp07\Source\cs\exercises\ex03\begin\BugSmak].• Right click on the Security Application Block | Profile node and select the New | Profile DatabaseProvider menu command.Page 111


Figure 3.1Create a New Profile Database Provider• Select the Profile Database Provider and set the Database property to EntLibQuickStarts.Figure 3.2Create a New Profile Database ProviderPage 112


• Select the Security Application Block node. Set the DefaultProfileInstance to Profile DatabaseProvider.Figure 3.3Create a New Profile Database Provider• Select the File | Save All menu command to save the configuration.Task 2 – Adding Personalization• In Visual Studio, select the Debug | Start menu command to run the application and sign in asStudent. Select the Tasks | Raise Bug menu command.Select the Options | Form Color menu command. Select a color in the Color dialog and click theOK button.You will use the profile provider to preserve color changes between application restarts.• Close the application.• Open the ProfileInfo.csfile in the Helper directory. The ProfileInfo class is a serializable class whichstores the profile information we would like to preserve.• To save the profile, add the following code to the SaveProfile method in the ProfileHelper.cs file.public static void SaveProfile(BaseForm taskForm){// TODO: Save Profile// Collect profile informationif (_profile == null) _profile = new ProfileInfo();Page 113


_profile.FormColor = taskForm.FormColor;_profile.TextColor = taskForm.TextColor;// Save Profile InformationIProfileProvider profileProvider;profileProvider = ProfileFactory.GetProfileProvider();profileProvider.SetProfile(Thread.CurrentPrincipal.Identity,_profile);}• To load the profile, add the following code to the LoadProfile method in the ProfileHelper.cs file.public static void LoadProfile(BaseForm taskForm){// TODO: Load & Apply Profile// Lookup profile informationif (_profile == null){IProfileProvider profileProvider;profileProvider = ProfileFactory.GetProfileProvider();_profile = (ProfileInfo) profileProvider.GetProfile(Thread.CurrentPrincipal.Identity);}}// Apply profileif (_profile == null) _profile = new ProfileInfo();taskForm.FormColor = _profile.FormColor;taskForm.TextColor = _profile.TextColor;Note: Note:The ProfileHelper keeps a static instance of the ProfileInfo class so that the profileinformation is only read once from the database.• Select the Debug | Start menu command to run the application and sign in as Student. Select theTasks | Raise Bug menu command.Select the Options | Form Color menu command. Select a color in the Color dialog and click theOK button.Exit and Restart the application and sign in as Student. Select the Tasks | Raise Bug menucommand and note that your color choices are preserved.Sign out, then re-sign in as Boss. Select the Tasks | Raise Bug menu command and note that thecolor choice has returned to the original value.Task 3 – More information• For more information on how to implement security in distributed applications, see the "UsingAdvanced Microsoft® .NET Application Security Techniques" course as part of the MasteringIndustrial Strength .NET series. This uses <strong>Enterprise</strong> <strong>Library</strong> to incorporate security in distributedsolutions, including building your own security providers and integrating with external authorizationsystems (e.g. authorization manager).Page 114


• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp07\Source\cs\exercises\ex03\end\BugSmak.sln]Lab SummaryIn this lab you performed the following exercises.• Secure an Application• Use Task Based Authorization In an Application• Application PersonalizationPage 115


Lab 8 C# Build your own BlockWARNING: This is a code heavy lab, please use the N,P and insert keys to navigate through thisexercise, otherwise you may get mouse cut & paste injurie.Your reward for finishing this lab (or skipping to the end to run the end solution.) will be to have a Clippit like characterthat knows when you are writing a letter.This lab uses the String Resource Tool for localization, which you have included here [C:\Microsoft Hands-On-Lab\HOLpnp08\Source\cs\exercises\].Please install this unless you have a newer or Microsoft version. The newest version canalways been found on the Readify project distributor Website.In this lab, you will abstract some functionality into a new application block. The steps to do this are:[Exercise 1]Create an abstract client API and plugin API, using the provider modelCreate configuration data for the provider factoryand a custom providerCreate a provider factory derived from the Configuration Block ProviderFactory classCreate aconcrete provider by implementing the plugin API.Create design time configuration assembly for the provider factoryconfiguration data and custom provider.[Exercise 2]Create configuration data for the included providers.Create design time configuration classes for the included providers.[Exercise 3]Implement external providers in a separate assembly.Other steps that would typically occur, which you won't cover in this lab:Build unit tests for the providers in the block.Document all the classes in the block (and integrate with VS.NET).Lab ObjectiveTime to complete: 60 minutesThe objectives of this lab are: abstract some functionality into an external application; support run-timeconfigurable multiple providers.• Review Current Application• Create a Notification Block• Add strongly typed configuration data• Add external providersExercise 1: Review Current ApplicationIn this exercise, you will investigate the piece of re-usable functionality that you want to turn into anapplication block.Page 116


Task 1 – Run the application• Open the Ex01 [C:\Microsoft Hands-On-Lab\HOL-pnp08\Source\cs\exercises\ex01\begin\Ex01.sln]file, and build the solution.• Run the application, and type the following (do not copy & paste):dear sir,thank you for your interest in patterns, you hope you enjoy <strong>Enterprise</strong><strong>Library</strong>.yours sincerely,p&p teamNote: See the file SmartWords.txt in the EditorApplication project for a list of the phrases detected.• As you can see, some phrases are automatically matched, and associated text is displayed in thestatus bar at the bottom.Task 2 – Review the Application code• Close the application.• Open the file EditorForm.cs, right-click and select View Code.• Find the method DisplayNotification. You will change the method by abstracting out the notificationto the user, and make it configurable at deployment.• Close Visual Studio.Exercise 2: Create a Notification BlockThis exercise will create the notification application block, and support adding external providers.Task 1 – Create the abstract API• Open the Ex02 [C:\Microsoft Hands-On-Lab\HOL-pnp08\Source\cs\exercises\ex02\begin\Ex02.sln]file, and build the solution.• You have created a new project, called Notification (FourthCoffee.Framework.Notification.dll), whichwill contain the API for our application block, as well as a single provider. You have createdskeleton files for all the files you need to implement.• Open the file INotificationProvider.cs in the Notification project. Add the following interface withinthe namespace. This will be our provider interface./// /// Interface exposed by any provider that supports notification/// public interface INotificationProvider : IConfigurationProvider{Page 117


Display a message to the user./// This method must not block execution, so if the message display/// will take any length of time, it should occur asynchronously./// /// The parent form to use when displaying themessage/// The message to display to the uservoid DisplayMessage(Form parent, string message);}Note: Note that the provider interface derives from IConfigurationProvider. This ensures that anyimplementation of the provider can be initialized through configuration. It also means, however, thatall clients of this API must also add a reference to the Configuration assembly.Task 2 – Create the configuration data for the block• Within the Configuration folder, open the NotificationSettings.cs file. Add the following class withinthe namespace definition:[XmlRoot("notificationSettings",Namespace=NotificationSettings.ConfigurationNamespace)]public class NotificationSettings{public const string ConfigurationNamespace ="http://www.fourthcoffee.com/framework/03-01-2005/notification";}public const string SectionName = "notificationConfiguration";Note: The XmlRoot attribute is important to specify the namespace of the XML as it appears whenserialized, required to make the XML unambiguous. You will reference these two public constantsthroughout the rest of the code.• Add the following properties and backing fields to the NotificationSettings class:private NotificationProviderDataCollection _notificationProviders = newNotificationProviderDataCollection();private string _defaultNotificationProviderName = null;[XmlArray(ElementName="notificationProviders")][XmlArrayItem(ElementName="notificationProvider",Type=typeof(NotificationProviderData))]public NotificationProviderDataCollection NotificationProviders{get { return _notificationProviders; }}[XmlAttribute("defaultProviderName")]public string DefaultNotificationProviderName{get { return _defaultNotificationProviderName; }set { _defaultNotificationProviderName = value; }}Note: The NotificationProviders property provides the data for the configured notification providers.Note that the type of the property should derive from ProviderDataCollection (defined in theConfiguration assembly), to support some of the design time functionality (as you shall showlater).The DefaultNotificationProviderName property will be used to allow the client of the block toPage 118


not specify a provider name, and use a default specified in configuration.The Xml* attributes arebeing used to customize the saved XML.• Open the NotificationProviderData.cs file, and implement the NotificationProviderData class. Thiswill contain the base class that all the configuration for the providers will derive from. This class isused for two purposes:To contain any common configuration data that all providers use. In this case you do not have anycommon configuration settings for all our providers (apart from Name and Type, which are definedin the ProviderData base class).This is also the location where you can add static XmlIncludeattributes that will allow the XmlSerializer to de-serialize derived classes. Of course, you can onlyadd XmlInclude attributes for classes in this assembly. You will see in the next exercise how toinclude additional classes for externally defined providers.[XmlInclude(typeof(CustomNotificationData))]public abstract class NotificationProviderData : ProviderData{}• Open the CustomNotificationData.cs file, and implement the CustomNotificationData class. Thisclass is responsible for holding the type information and supporting additional weakly typedconfiguration data for providers which do not have design time support.[XmlRoot("notificationProvider",Namespace=NotificationSettings.ConfigurationNamespace)]public class CustomNotificationData : NotificationProviderData{private string _typeName;private NameValueItemCollection _attributes = newNameValueItemCollection();[XmlAttribute("type")]public override string TypeName{get { return _typeName; }set { _typeName = value; }}[XmlElement("Attributes")]public NameValueItemCollection Attributes{get { return this._attributes; }}}Note: NOTE: It is very important that the XmlRoot attribute exists, and specifies a namespace. If anamespace is not specified, then the configuration data cannot be de-serialized by theXmlSerializer (as the de-serializer will be looking for a blank namespace, when the XMLnamespace will actually be set by the containing XML elements).• Open NotificationProviderDataCollection.cs. Implement the provider collection class:public class NotificationProviderDataCollection : ProviderDataCollection{public NotificationProviderData this[int index]{get { return (NotificationProviderData) GetProvider(index); }set { SetProvider(index, value); }}public NotificationProviderData this[string name]Page 119


{get { return (NotificationProviderData) GetProvider(name); }set { SetProvider(name, value); }}public void Add(NotificationProviderData providerData){AddProvider(providerData);}}Note: This class is used by the Settings class to maintain the list of provider data. It should derivefrom the ProviderDataCollection class (rather than, for example, CollectionBase), because theconfiguration design time support can provide additional functionality with ProviderDataCollectionbased classes.Task 3 – Create the configuration view• The configuration view class is responsible for querying the configuration subsystem for the currentconfiguration information. By using this class, the provider will automatically get the latestconfiguration information, even if the configuration has changed on disk while the application isrunning.• Open the NotificationConfigurationView.csfile. Implement the NotificationConfigurationView classwith the following code:public class NotificationConfigurationView : ConfigurationView{public NotificationConfigurationView(ConfigurationContext context) :base(context){}public virtual NotificationSettings GetNotificationSettings(){return (NotificationSettings)ConfigurationContext.GetConfiguration(NotificationSettings.SectionName);}public virtual NotificationProviderDataGetNotificationProviderData(string providerName){ArgumentValidation.CheckForNullReference(providerName,"sinkName");ArgumentValidation.CheckForEmptyString(providerName,"sinkName");NotificationSettings settings = GetNotificationSettings();NotificationProviderData sinkData =settings.NotificationProviders[providerName];if (null == sinkData){throw newConfigurationException(SR.NoSuchProviderDefined(providerName));}return sinkData;}}Page 120


Note: The ConfigurationContext class will load and cache the configuration section defined in theapplication configuration. It will also automatically re-load the configuration on demand if it ischanged in the source (e.g. on disk).Task 4 – Create the notification provider factory• OpenNotificationProviderFactory.cs, and add the following class:public class NotificationProviderFactory : ProviderFactory{public NotificationProviderFactory() :this(ConfigurationManager.GetCurrentContext()){}public NotificationProviderFactory(ConfigurationContextconfigurationContext) :base(SR.HandlerFactoryName, configurationContext,typeof(INotificationProvider)){}private NotificationConfigurationView GetConfigurationView(){return newNotificationConfigurationView(this.ConfigurationContext);}// TODO: Implement protected overloads to provide type information}// TODO: Implement strongly-typed creation methods• Add the following methods, which are required for the base class factory implementation to work:protected override ConfigurationView CreateConfigurationView(){return GetConfigurationView();}protected override string GetDefaultInstanceName(){returnGetConfigurationView().GetNotificationSettings().DefaultNotificationProviderName;}protected override Type GetConfigurationType(string configurationName){string typeName =GetConfigurationView().GetNotificationProviderData(configurationName).TypeName;return base.GetType(typeName);}• Add the following methods, which provide strongly typed creation of the providers:public INotificationProvider GetNotificationProvider(){return (INotificationProvider) base.CreateDefaultInstance();}Page 121


public INotificationProvider GetNotificationProvider(stringproviderName){return (INotificationProvider) base.CreateInstance(providerName);}Task 5 – Create a static Factory class for one-line creation of providers• Open the NotificationFactory.cs file, and create the following NotificationFactory class, whichprovides static helper methods to create an instance of the notification provider (default providerand named provider):public sealed class NotificationFactory{private NotificationFactory(){}public static INotificationProvider GetNotificationProvider(){NotificationProviderFactory factory = newNotificationProviderFactory();return factory.GetNotificationProvider();}public static INotificationProvider GetNotificationProvider(stringproviderName){NotificationProviderFactory factory = newNotificationProviderFactory();return factory.GetNotificationProvider(providerName);}}Task 6 – Implement a weakly typed provider• Open StatusBarNotificationProvider.cs. Within the file, you have partially implemented the StatusBar Notification Provider. This provider will search for a status bar in the form which is asking fornotification (or its parent), and then display the message for a certain length of time in the statusbar.• Derive the class from ConfgurationProvider, and implement the INotificationProvider interface(changed code in bold):public class StatusBarNotificationProvider : ConfigurationProvider,INotificationProviderNote: The ConfigurationProvider base class implements the IConfigurationProvider interface, andmanages the Configuration Name of the provider. This class must then override the Initializemethod of the ConfigurationProvider to be able to obtain configuration data later.• Implement the Initialize method with the following code:NotificationConfigurationView config = null;public override void Initialize(ConfigurationView configurationView){ArgumentValidation.CheckExpectedType(configurationView,Page 122


typeof(NotificationConfigurationView));config = (NotificationConfigurationView) configurationView;}• Implement the DisplayMessage method (from INotificationProvider) with the following code:public void DisplayMessage(Form parent, string message){// Default display timeoutint displayTimeout = 2000;CustomNotificationData configData =(CustomNotificationData)config.GetNotificationProviderData(ConfigurationName);if (configData.Attributes.GetNameValueItem("DisplayTimeout") !=null){displayTimeout =int.Parse(configData.Attributes.GetNameValueItem("DisplayTimeout").Value);}StatusBar bar = SearchForStatusBar(parent);}StatusBarManager manager = new StatusBarManager(bar);manager.Display(message, displayTimeout);Note: This code obtains the current display timeout value using the Custom notificationconfiguration data, if set, finds the status bar, and then uses the StatusBarManager class to displaythe message.Task 7 – Add design time support• You have now implemented all the run-time provider infrastructure that you need. You now need tocreate the design-time support for the configuration console. Build the solution and fix any compileerrors at this point.• Open NotificationProviderNode.cs in the Notification.Configuration.Design project. Implement theNotification Provider Node class, which is the design time representation of theNotificationProviderData class:[Image(typeof(NotificationProviderNode))]public abstract class NotificationProviderNode : ConfigurationNode{[Browsable(false)]public abstract NotificationProviderData NotificationProviderData{get;}protected override void OnSited(){base.OnSited();Site.Name = NotificationProviderData.Name;}protected override void OnRenamed(ConfigurationNodeChangedEventArgse){base.OnRenamed (e);Page 123


}}NotificationProviderData.Name = e.Node.Name;Note: This class is abstract, as each of the provider types (at this point there is only the CustomNotification Provider), will have their own concrete xyzNode class that is used to manipulate theirdata.The Image tag is used to provide the bitmap that will be displayed in the tree. In this case, abitmap named NotificationProviderNode.bmp should be in the project as an embeddedresource.The OnSited and OnRenamed methods are use to pull and push the Name informationfrom/to the underlying NotificationProviderData instance (which is provided by the concretexyzNode class).• Open the CustomNotificationProviderNode.cs file, and implement theCustomNotificationProviderNode class, which is used to manipulate the CustomNotificationDataconfiguration class.public class CustomNotificationProviderNode : NotificationProviderNode{private CustomNotificationData customNotificationData;public override NotificationProviderData NotificationProviderData{get{return customNotificationData;}}// Initializes node with default data.public CustomNotificationProviderNode(){this.customNotificationData = new CustomNotificationData();this.customNotificationData.Name =SR.DefaultCustomNotificationProviderNodeName;}// Initializes node with loaded data.public CustomNotificationProviderNode(CustomNotificationDatacustomNotificationData){this.customNotificationData = customNotificationData;}// TODO: Add properties that provide design-time access toconfiguration class}Note: This class is the design-time access class for the custom notification provider's configurationdata.• Add the Attributes property to the CustomNotificationProviderNode class:[SRDescription(SR.Keys.NotificationProviderAdditionalPropertiesDescription)][SRCategory(SR.Keys.CategoryGeneral)]public NameValueItemCollection Attributes{get { return customNotificationData.Attributes; }}Page 124


Note: The SRDescription and SRCategory attributes are attribute classes that use resources tospecify the Description and Category at runtime of the particular property in the property grid. Thisis used to support localization.The SR class is code-generated from the SR.strings file, which isalso located in the same project. The Custom Tool that is used to generate the strongly-typedresource class can be downloaded from the link at the top of this lab.• Add the Type Property to the Same Class:[Required][Editor(typeof(TypeSelectorEditor), typeof(UITypeEditor))][BaseType(typeof(INotificationProvider))][SRDescription(SR.Keys.NotificationProviderTypeDescription)][SRCategory(SR.Keys.CategoryGeneral)]public virtual string TypeName{get { return customNotificationData.TypeName; }set { customNotificationData.TypeName = value; }}Note: This property uses the EditorAttribute (from System.ComponentModel) to specify the editor touse when displaying this property in the property grid. In this case, the editor is the <strong>Enterprise</strong><strong>Library</strong> type selection dialog, which uses the BaseType attribute to filter the types available.TheRequired attribute (from the Configuration block) is used by Configuration Block validation to ensurethat the property has been set before the user can save the configuration in the console.• Open the NotificationSettingsNode.cs, and implement the NotificationSettingsNode class, whichrepresents the root node of the Notification Block:[Image(typeof (NotificationSettingsNode))]public class NotificationSettingsNode : ConfigurationNode{private NotificationSettings notificationSettings;public NotificationSettingsNode() : this(new NotificationSettings()){}public NotificationSettingsNode(NotificationSettingsnotificationSettings){this.notificationSettings = notificationSettings;}/// /// The configured name./// [ReadOnly(true)]public override string Name{get{return base.Name;}set{base.Name = value;}}/// /// Retrieves configuration data based on the current state of thenode./// /// Configuration data for this node.Page 125


[Browsable(false)]public virtual NotificationSettings NotificationSettings{get{notificationSettings.NotificationProviders.Clear();foreach (NotificationProviderNode node in this.Nodes){notificationSettings.NotificationProviders.Add(node.NotificationProviderData);}return notificationSettings;}}}• During the life-cycle of a node in the configuration console, it has a few chances to change the hostenvironment. When this node is added to the console (e.g. when an application configuration file isloaded), you need to add all the nodes for each of the notification providers configured. Similarly,when given the chance to add menu items, you want to add a menu item under this node to createnew notification providers. Add the following code to the NotificationSettingsNode class:protected override void OnSited(){base.OnSited ();Site.Name = SR.DefaultNotificationSettingsNodeName;CreateDynamicNodes(notificationSettings.NotificationProviders);}protected override void OnAddMenuItems(){base.OnAddMenuItems ();CreateDynamicMenuItems(typeof(NotificationProviderNode));}Note: The CreateDynamicNodes call will iterate through the NotificationProviders collection, andcreate nodes of the appropriate type based on the type of the instance of the object in thatcollection. The mapping from configuration data type to node data type is performed within aConfigurationDesignManager (as you will do later).CreateDynamicMenuItems, similarly, will iteratethrough any registered node types that are derived from the given type, and create menu items forthem.• You need to add a property to this class to allow the user to select a default notification provider.Once selected, you need to ensure that the name of the provider stored within theNotificationSettings class always matches the name of the provider within its ownNotificationProviderData class. You also need to take care of the case where the user selects aprovider as the default, and then deletes it. Unfortunately, there is no succinct way of doingthis, and so the following code is quite complex. In future versions of <strong>Enterprise</strong> <strong>Library</strong>, there maybe a shortcut way of implementing it.Add the following code to the NotificationSettingsNode class:NotificationProviderNode defaultNotificationProviderNode = null;[Editor(typeof(ReferenceEditor), typeof(UITypeEditor))][ReferenceType(typeof(NotificationProviderNode))][SRCategory(SR.Keys.CategoryGeneral)][SRDescription(SR.Keys.DefaultProviderDescription)]Page 126


public NotificationProviderNode DefaultNotificationInstance{get { return defaultNotificationProviderNode; }set{ILinkNodeService service = GetService(typeof(ILinkNodeService))as ILinkNodeService;Debug.Assert(service != null, "Could not get theILinkNodeService");defaultNotificationProviderNode =(NotificationProviderNode)service.CreateReference(defaultNotificationProviderNode,value,newConfigurationNodeChangedEventHandler(OnNotificationDefaultProviderRemoved),newConfigurationNodeChangedEventHandler(OnNotificationDefaultProviderRenamed));this.notificationSettings.DefaultNotificationProviderName =(defaultNotificationProviderNode != null) ?this.defaultNotificationProviderNode.Name : string.Empty;}}private void OnNotificationDefaultProviderRenamed(object sender,ConfigurationNodeChangedEventArgs args){this.notificationSettings.DefaultNotificationProviderName =(defaultNotificationProviderNode != null) ?this.defaultNotificationProviderNode.Name : string.Empty;}private void OnNotificationDefaultProviderRemoved(object sender,ConfigurationNodeChangedEventArgs args){this.defaultNotificationProviderNode = null;}private void ResolveDefaultNotificationNode(){if ((notificationSettings.DefaultNotificationProviderName == null)|| (notificationSettings.DefaultNotificationProviderName.Length == 0))return;DefaultNotificationInstance = Hierarchy.FindNodeByName(this,notificationSettings.DefaultNotificationProviderName) asNotificationProviderNode;}public override void ResolveNodeReferences(){base.ResolveNodeReferences ();ResolveDefaultNotificationNode();}Note: The Editor attribute will create a drop-down, which is populated with node instances whichderive from NotificationProviderNode (specified in the ReferenceType attribute).• The last class you need to implement is called a Configuration Design Manager. It is responsible forloading the configuration into the designer, registering node types and XML includes, and savingthe configuration.Open NotificationConfigurationDesignManager.cs, and un-comment thePage 127


NotificationConfigurationDesignManager class.Note: The CreateCommands method is responsible for adding the new notification section menuitem to the root node of the application in the console.The RegisterNodeTypes method isresponsible for registering settings and provider node types, which allow the dynamic menu itemcreation to work.• Finally, in order for the configuration console to use the Configuration Design Manager youcreated, you need to put the assembly in the same directory as the configuration console, and usean assembly attribute to provide the type of the design manager.You have already changed the build directory to be the <strong>Enterprise</strong> <strong>Library</strong> bin directory (you cancheck in the project properties window).Open the AssemblyInfo.cs file in the Notification.Configuration.Design project, and add the followingattribute at the bottom:[assembly :ConfigurationDesignManager(typeof(NotificationConfigurationDesignManager))]Task 8 – Run the configuration console• You have now completed the framework for the new application block. You now have an applicationblock that will appear within the configuration console, supports run-time configuration re-loads, andthat you can extend with additional providers at run-time.• Build the solution, and ensure that it built successfully.• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.• Select File | Open Application and browse to the App.config file located here [C:\Microsoft Hands-On-Lab\HOLpnp08\Source\cs\exercises\ex02\begin\EditorApplication][ex02\begin\EditorApplication].• Right-click over the Application node, and add a new Notification section.Page 128


• Add a new custom notification provider.Figure 2.1Create a new Notification ProviderPage 129


Figure 2.2Create a new Custom Notification Provider• Click on the TypeName property. Click the elipsis, and select the typeStatusBarNotificationProvider.Figure 2.3Type Selector• Click on the Attributes property, and add a new attribute:Page 130


Note: Name: DisplayTimeoutValue: 3000Figure 2.4Display Timeout• Select the Notification section, and set the default provider to Custom Notification Provider.Figure 2.5<strong>Enterprise</strong> <strong>Library</strong> Configuration• Select the File | Save All menu command to save the configuration.Page 131


Task 9 – Change the client application• You have already created post-build steps, and added the required references and usingstatements.Within the EditorApplication project, right-click over the EditorForm.cs file and select View Code.Change the DisplayNotification method to (modified code in bold):private void DisplayNotification(string description){try{INotificationProvider provider =NotificationFactory.GetNotificationProvider();provider.DisplayMessage(this, description);}catch (Exception ex){MessageBox.Show(ex.ToString());}}Task 10 – Run the application• Run the application, and type the following (do not copy & paste):dear sir,thank you for your interest in patterns, you hope you enjoy <strong>Enterprise</strong><strong>Library</strong>.yours sincerely,p&p teamNote: See the file SmartWords.txt in the EditorApplication project for a list of the phrases detected.• Close the Configuration console, if it is running. Re-open it, but this time open the applicationconfiguration file from the ex02\begin\EditorApplication\bin\Debug directory (here). Try changing theDisplayTimeout attribute value in the configuration console while the application is running. It maytake up to 15 seconds for a change to take effect.• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp08\Source\cs\exercises\ex02\end\Ex02.sln]Exercise 3: Add strongly typed configuration dataIn this exercise, you will change the status bar provider to use strongly typed configuration data.Task 1 – Add run-time configuration• Open the Ex03 [C:\Microsoft Hands-On-Lab\HOL-pnp08\Source\cs\exercises\ex03\begin\Ex03.sln]file, and build the solution.Page 132


• Open the new file StatusBarNotificationData.cs, which is in the Configuration folder of theNotification project.Implement the StatusBarNotificationData class with the following code:[XmlRoot("notificationProvider",Namespace=NotificationSettings.ConfigurationNamespace)]public class StatusBarNotificationData : NotificationProviderData{[XmlIgnore]public override string TypeName{get { returntypeof(StatusBarNotificationProvider).AssemblyQualifiedName; }set { }}int _displayTimeout = 2000;}[XmlElement("displayTimeout")]public int DisplayTimeout{get { return _displayTimeout; }set { _displayTimeout = value; }}Note: The XmlRoot attribute is required to ensure that the namespace of the class is set, for thesame reasons as the CustomNotificationData above.The XmlIgnore attribute is used becausethe type of the provider does not need to be serialized, as it will always be the same. (TheTypeName property is used at runtime by the notification provider factory to specify the concretetype of the provider).The DisplayTimeout property will contain the timeout in ms, with a default of2000 ms.• Open the file NotificationProviderData.cs, and add the following XmlInclude attribute before theclass definition (inserted code in bold):[XmlInclude(typeof(CustomNotificationData))][XmlInclude(typeof(StatusBarNotificationData))]public abstract class NotificationProviderData : ProviderData{}Note: This is used to allow the Xml Serializer infrastructure to de-serialize theStatusBarNotificationData class when de-serializing the NotificationProvideDataCollection instance.• Open the file StatusBarNotificationProvider.cs, and change the DisplayMessage method as below(modified code in bold):public void DisplayMessage(Form parent, string message){StatusBarNotificationData configData =(StatusBarNotificationData)config.GetNotificationProviderData(ConfigurationName);int displayTimeout = configData.DisplayTimeout;StatusBar bar = SearchForStatusBar(parent);StatusBarManager manager = new StatusBarManager(bar);Page 133


}manager.Display(message, displayTimeout);Task 2 – Add design time configuration support• Within the Notification.Configuration.Design project, open the new fileStatusBarNotificationNode.cs, and implement the StatusBarNotificationNode class:public class StatusBarNotificationNode : NotificationProviderNode{private StatusBarNotificationData statusBarNotificationData;public override NotificationProviderData NotificationProviderData{get{return statusBarNotificationData;}}public StatusBarNotificationNode(){this.statusBarNotificationData = newStatusBarNotificationData();this.statusBarNotificationData.Name =SR.DefaultSatusBarNotificationNodeName;}public StatusBarNotificationNode(StatusBarNotificationDatastatusBarNotificationData){this.statusBarNotificationData = statusBarNotificationData;}[SRCategory(SR.Keys.CategoryBehavior)][SRDescription(SR.Keys.StatusBarTimoutDescription)]public int DisplayTimeout{get { return statusBarNotificationData.DisplayTimeout;}set { statusBarNotificationData.DisplayTimeout = value;}}}Note: In order to implement the design time support, the Provider Node class must:Supply theconfiguration data instance that contains the Name to its base classProvide constructors for a newinstance (adding a new provider), and for loading from a configuration file.Provide properties foreach of the data properties that should be exposed, along with optional design time attributes.• Open the file NotificationConfigurationDesignManager.cs, and add the following code at the end ofthe RegisterNodeTypes method (inserted code in bold).private static void RegisterNodeTypes(IServiceProvider serviceProvider){INodeCreationService nodeCreationService =ServiceHelper.GetNodeCreationService(serviceProvider);Type nodeType = typeof(CustomNotificationProviderNode);NodeCreationEntry entry =NodeCreationEntry.CreateNodeCreationEntryWithMultiples(newAddChildNodeCommand(serviceProvider, nodeType), nodeType,typeof(CustomNotificationData),SR.DefaultCustomNotificationProviderNodeName);nodeCreationService.AddNodeCreationEntry(entry);Page 134


nodeType = typeof(StatusBarNotificationNode);entry = NodeCreationEntry.CreateNodeCreationEntryWithMultiples(newAddChildNodeCommand(serviceProvider, nodeType), nodeType,typeof(StatusBarNotificationData),SR.DefaultSatusBarNotificationNodeName);nodeCreationService.AddNodeCreationEntry(entry);}Note: This registers the new node (and associated data class) with the configuration console.Task 3 – Build and Configure• Build the application, and check that there are no errors.• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.• Select File | Open Application and browse to the App.configfile located here [C:\Microsoft Hands-On-Lab\HOL-pnp08\Source\cs\exercises\ex03\begin\EditorApplication].• There is already a notification section configured. Add a new Status Bar Notification.• Set the DisplayTimeout to 3000.• Select the Notification section, and set the default provider to Status Bar Notification.Figure 3.1Create a new Status Bar Notification• Select the File | Save All menu command to save the configuration.Page 135


• Run the application, and test some the smart words.• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp08\Source\cs\exercises\ex03\end\Ex03.sln]Exercise 4: Add external providersIn this exercise you will add two providers for notification, one based on MS Agent, and the other usinga scrolling title bar to display information.Task 1 – Review the extended providers• Open the Ex04 [C:\Microsoft Hands-On-Lab\HOL-pnp08\Source\cs\exercises\ex04\begin\Ex04.sln]file, and build the solution.• In the Notification.Extended project are two new providers, one to scroll the message in the formstitle bar, the other uses the Microsoft Agent technology to display the messages.• Open the FormTitleNotificationNode class in the Notification.Extended.Configuration.Design project.Note: This class demonstrates using the validation attributes that are part of the <strong>Enterprise</strong> <strong>Library</strong>configuration validation namespace (AssertRange), as well as showing mapping between datavalues in the runtime configuration, and design-time views.• Open the AgentNotificationNode class.Note: This class demonstrates using the FilteredFileNameEditor which allows browsing for afilename in the property grid, as well as the FileValidation attribute which will check that the fileexists.Task 2 – View the registration code for the external providers• The one difference when creating providers in external assemblies, compared to what you did withthe status bar notification provider, is managing the XML serialization.With providers defined in external assemblies, it is necessary to tell the configuration console toinclude those types used within the configuration for XML Serialization. This is done in theConfiguration Design Manager.Open the file ConfigurationDesignManager.cs within the Notification.Extended.Configuration.Designproject.The following code in RegisterXmlIncludeTypes method registers the types for Xml inclusion:private static void RegisterXmlIncludeTypes(IServiceProviderserviceProvider){IXmlIncludeTypeService xmlIncludeTypeService =serviceProvider.GetService(typeof(IXmlIncludeTypeService)) asIXmlIncludeTypeService;xmlIncludeTypeService.AddXmlIncludeType(NotificationSettings.SectionNamePage 136


, typeof(FormTitleNotificationData));xmlIncludeTypeService.AddXmlIncludeType(NotificationSettings.SectionName, typeof(AgentNotificationData));}Task 3 – Configure the application• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.• Select File | Open Application and browse to the App.configfile located here [C:\Microsoft Hands-On-Lab\HOLpnp08\Source\cs\exercises\ex04\begin\EditorApplication][ex04\begin\EditorApplication].• Create a new notification provider for each of the types, Agent Notification & Form Title Notification.• Set the default notification provider to be the Agent provider.• Close the Configuration Console.Task 4 – Add a reference to the extended notification assembly from the application• Add a project reference to the EditorApplication by Project | Add Reference, and selecting theNotification.Extended project.Task 5 – Run the application• Build and Run the application. Test that the Agent appears when typing in any of the magicphrases.• Run the enterprise console, and this time open the application configuration file from the runtimedirectory of the application (here [C:\Microsoft Hands-On-Lab\HOLpnp08\Source\cs\exercises\ex04\begin\EditorApplication\bin\Debug\])[ex04\begin\EditorApplication\bin\Debug\App.config].• Change the default provider, save, wait up to fifteen seconds, and then type some more magicwords (see SmartWords.txt in the EditorApplication project for the list). You will see the newprovider setting used. You can even change the agent that is being used on the fly, by selecting anew agent file.If you have Microsoft office installed, you can even use Clippit, which can be found in a directorylike \Microsoft Office\OFFICE11.Task 6 – For more information• For further information on the application of best practises, enterprise library and building enterprisescale applications, see the Mastering Industrial Strength .NET course, which also includes avalidation application block (in the Service Oriented Architecture section of the course).Page 137


• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp08\Source\cs\exercises\ex04\end\Ex04.sln]Lab SummaryIn this lab you performed the following exercises.• Review Current Application• Create a Notification Block• Add strongly typed configuration data• Add external providersPage 138


Lab 1 VB.NET Configuration BlockThis lab demonstrates the use of the <strong>Enterprise</strong> <strong>Library</strong> Configuration Application Block.Lab ObjectiveTime to complete: 30 minutesThe objectives of this lab are: read and write a custom configuration section for your application;monitor a configuration file for changes.• Writing custom settings to a configuration file• Reading custom settings from a Configuration File• Configuration File WatcherExercise 1: Writing custom settings to a configuration fileIn this exercise you will write custom configuration settings to a configuration file. You will configure theconfiguration via the <strong>Enterprise</strong> <strong>Library</strong> Configuration Console.Task 1 – Introducing the application• Open the EnoughPI [C:\Microsoft Hands-On-Lab\HOLpnp01\Source\vb\exercises\ex01\begin\EnoughPI.sln]file, and build the solution.• Select the Debug | Start menu command to run the application.The EnoughPI application calculates the digits of pi (p, the ratio of the circumference of a circle toits diameter). Enter your desired precision via the NumericUpDown control and click the Calculatebutton. Be prepared to wait if you want more than 500 digits of precision.Page 139


Figure 1.1Never Enough PI• The user interface is configurable. Select the Options | Background Color menu command tochange the background color. Select the Options | Font menu command to change the form font.The number of digits of pi to calculate is also selectable via the NumericUpDown control.Task 2 – Adding a Configuration Data Class• Add the following code to the EnoughPIData class in the EnoughPI.Configuration project.Public Class EnoughPIDataPrivate m_digits As IntegerPrivate m_backColor As ColorPrivate m_font As FontDataPublic Sub New()End SubPublic Sub New(ByVal digits As Integer, ByVal backColor AsColor, ByVal font As Font)Me.Digits = digitsMe.BackColor = backColorMe.Font = fontEnd SubPublic Property Digits() As IntegerGetReturn Me.m_digitsEnd GetSet(ByVal Value As Integer)Me.m_digits = ValueEnd SetEnd PropertyPage 140


_Public Property BackColor() As ColorGetReturn Me.m_backColorEnd GetSet(ByVal Value As Color)Me.m_backColor = ValueEnd SetEnd Property _Public Property HtmlBackColor() As StringGetReturn ColorTranslator.ToHtml(Me.m_backColor)End GetSet(ByVal Value As String)Me.m_backColor = ColorTranslator.FromHtml(Value)End SetEnd Property _Public Property Font() As FontGetReturn New Font(Me.FontData.Name, _Me.FontData.Size, _CType(Me.FontData.Style, FontStyle) _)End GetSet(ByVal Value As Font)Me.FontData = New FontDataMe.FontData.Name = Value.NameMe.FontData.Size = Value.SizeMe.FontData.Style = Convert.ToInt32(Value.Style)End SetEnd Property _Public Property FontData() As FontDataGetReturn Me.m_fontEnd GetSet(ByVal Value As FontData)Me.m_font = ValueEnd SetEnd PropertyEnd ClassNote: To save the configuration data you create a class to hold the configuration data. You thenuse the Configuration Application Block infrastructure to serialize the data to a configurationfile. Note the classes are XML Serializable (have a default public constructor and public properties)as you will use the XML Serialization infrastructure of the Configuration Application Block.• Select Build | Build Solution to compile the complete solution.Task 3 – Adding a Custom Configuration Section• Add a new Application configuration file (App.config) to the EnoughPI project. Click on theEnoughPI project. Select the File | Add New Item menu command. Select Application configurationPage 141


file template. Leave the Name as App.config.Figure 1.2Add New Item• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.• Select File | Open Application and browse to the App.configfile located here [C:\Microsoft Hands-On-Lab\HOL-pnp01\Source\vb\exercises\ex01\begin\EnoughPI].• Right click on the Application and select New | Configuration Application Block.Page 142


Figure 1.3Create new Configuration Application Block• Right click on the Configuration Application Block and select New | Configuration Section.Figure 1.4Create a new Configuration Application Block• Rename the configuration section as EnoughPISettings.Figure 1.5<strong>Enterprise</strong> <strong>Library</strong> ConfigurationPage 143


• Right click on the EnoughPISettings configuration section and select New | XML File StorageProvider.Figure 1.6Create a new XML File Storage Provider• Enter "EnoughPISettings.config"in the FileName field of the XML File Storage Provider.Figure 1.7Create a new XML File Storage ProviderPage 144


Note: The configuration settings will be expected to be stored as XML and found in a file namedEnoughPISettings.config in the same directory as the application executable.• Select the File | Save All menu command to save the configuration.• Select the EnoughPI project in Visual Studio. Select the Project | Show All Files menu command.Note an empty EnoughPISettings.configfile has been created for you.• In the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool, right click on the EnoughPISettings configurationsection and select New | XML Serializer Transformer.Figure 1.8Create a new Xml Serializer TransformerNote: The XML Serializer Transformer stands between the application and the XML file storageto serialize and deserialize our custom configuration data.• Select the File | Save All menu command to save the configuration. Close the <strong>Enterprise</strong> <strong>Library</strong>Configuration tool.Task 4 – Adding a Post-Build step to a VB.NET Project• The configuration settings file is expected to be found in the same directory as the applicationconfiguration file. Therefore you need to copy the EnoughPISettings.config configuration file to thebin directory before running the application. You could copy the file manually, but it would bepreferable to automate the copy as part of the build process.VB.NET does not provide support for pre and postbuild events, but you can achieve the same resultby adding a C# class library.Page 145


If you do not have C# installed, you can accomplish a similar effect by using the PrePostBuildRulesAdd-in you can get from Visual Studio .NET 2003 Automation Samples. See these tips for otheralternatives.Select the File | Add Project | New Project menu command.• In the New Project dialog select a C# Class <strong>Library</strong> Project, name it Post-Build and click OK.Note:Figure 1.9• Right click on the new Post-Build project and select Properties from the popup menu.• In the project properties dialog select Build Events under Common Properties and set the Post-buildevent command line property to the value below.copy "$(SolutionDir)\EnoughPI\*.config" "$(SolutionDir)\EnoughPI\bin" >NulPage 146


Figure 1.10Set Post-buildNote: Redirecting the output to the Nul device will prevent the message 1 file(s) copied fromappearing in your build output.• Select the Build | Build Solution menu command. The last thing you will see in the build output isthe message:Performing Post-Build Event.If the post-build event fails for some reason, this will be shown as a build error.Note: Every time you build the application the post build event will run, copying all the config files inthe EnoughPI directory to the bin directory.Task 5 – Saving Custom Configuration Settings• Select the EnoughPI project. Select the Project | Add Reference menu command. Select theBrowse button and select the following assemblies located here [C:\Program Files\Microsoft<strong>Enterprise</strong> <strong>Library</strong>\bin].Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Common.dll,Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Configuration.dll.• Select the MainForm.vbfile. Select the View | Code menu command. Add the following namespaceinclusion to the list of namespaces.Imports Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Configuration• Add the following code to the SaveConfiguration method in the MainForm.vbfile.Page 147


Private Sub SaveConfiguration()' Collect Configuration DataDim configData As EnoughPIDataconfigData = NewEnoughPIData(Convert.ToInt32(Me.numericUpDown1.Value), _Me.panel2.BackColor, Me.Font)' Save Configuration DataConfigurationManager.WriteConfiguration("EnoughPISettings",configData)End SubNote: The code collects the custom configuration you'd like to save by populating an EnoughPIDataobject. The ConfigurationManager is then used to write the configuration to the data store identifiedin the EnoughPISettings configuration section.• Select the Debug | Start menu command to run the application. Customize the interface bymodifying the background color and font (selected off the Options menu). Select the Configuration |Save menu command to save the configuration.Figure 1.11Never Enough PI• View the configuration settings file (EnoughPISettings.config) in the bin [C:\Microsoft Hands-On-Lab\HOL-pnp01\Source\vb\exercises\ex01\begin\EnoughPI\bin] directory. The file should look verysimilar to the XML below (depending on your selected background color and font).Page 148


456#FFFF80Century Gothic11.252Note: Note the file contains XML and the root element matches the configuration section name.The xmlSerializerSection element is used by the XML Serializer Transformer to identify theserialized type.• Close Visual Studio.• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp01\Source\vb\exercises\ex01\end\EnoughPI.sln]Exercise 2: Reading custom settings from a Configuration FileIn this exercise you will read custom configuration settings from a configuration file.Task 1 – Loading Custom Configuration Settings• Open the EnoughPI [C:\Microsoft Hands-On-Lab\HOLpnp01\Source\vb\exercises\ex02\begin\EnoughPI.sln]file, and build the solution.• Select the Debug | Start menu command (or press F5) to run the application. Note the defaultbackground color is blue, and the default font is Microsoft Sans Serif, 12pt.• Add the following code to the LoadConfiguration method in the MainForm.vbfile.Private Sub LoadConfiguration()' Read Configuration DataDim configData As EnoughPIDataconfigData =CType(ConfigurationManager.GetConfiguration("EnoughPISettings"), _EnoughPIData)End Sub' Apply Configuration DataMe.numericUpDown1.Value = configData.DigitsMe.panel2.BackColor = configData.BackColorMe.Font = configData.FontMe.Refresh()Note: The ConfigurationManager is used to read the configuration settings from the data storeidentified in the EnoughPISettings configuration section. The settings are then applied to the form.• For performance reasons, the Configuration Application Block caches the configuration sections (sothat file access is minimized). It will automatically clear the cache when the file changes (after theconfiguration polling interval).Page 149


• Run up two copies of the application without debugging (Select the Debug | Start WithoutDebugging menu command or press Ctrl+F5). In one application customize the interface bymodifying the background color and font (selected off the Options menu), and select theConfiguration | Save menu command.In the second application, wait 15 seconds, then select Configuration | Load to reload the settings.The second application should now look the same as the first.Private Sub LoadConfiguration()ConfigurationManager.ClearSingletonSectionCache("EnoughPISettings")' Read Configuration DataDim configData As EnoughPIDataconfigData =CType(ConfigurationManager.GetConfiguration("EnoughPISettings"), _EnoughPIData)End Sub' Apply Configuration DataMe.numericUpDown1.Value = configData.DigitsMe.panel2.BackColor = configData.BackColorMe.Font = configData.FontMe.Refresh()Note: Why wait 15 seconds? The Configuration Application Block infrastructure caches theconfiguration file settings and relies on file watcher to inform it when the configuration files change.The watcher works on a polling interval of 15 seconds. Hence if you had not waited 15seconds you may have been reading the old cached settings rather than the new savedsettings. If you had desired to always read the data from the file, and never use the cachedsettings you could clear the cache before getting the configuration data (see the code above).• Close Visual Studio.Exercise 3: Configuration File WatcherIn this exercise you will monitor changes to the configuration file and reload the configuration settings ifthe file is modified.Task 1 – Handling the ConfigurationChanged Event• Open the EnoughPI [C:\Microsoft Hands-On-Lab\HOLpnp01\Source\vb\exercises\ex03\begin\EnoughPI.sln]file, and build the solution.• Often you would like an application to monitor configuration settings, and automatically load newsettings. The XML File Storage Provider is watching the configuration file, and theConfgurationManager will notify us when the file is changed by raising an event. Handle theConfigurationChanged event by adding the following code to the MainForm_Load method in theMainForm.vbfile.Page 150


Private Sub MainForm_Load(ByVal sender As Object, _ByVal e As System.EventArgs) Handles MyBase.LoadMe.LoadConfiguration()AddHandler ConfigurationManager.ConfigurationChanged, AddressOfConfigurationChangedEnd Sub• Add the following method to the MainForm class.Private Sub ConfigurationChanged(ByVal sender As Object, _ByVal e As ConfigurationChangedEventArgs)End SubMe.Invoke(New MethodInvoker(AddressOf Me.LoadConfiguration))Note: When a change is detected on the configuration file, you reload the configuration settings.Note that the thread that the event is fired on is not the main UI thread, so it is necessary to use theInvoke method to marshal the call onto the main thread before updating any UI elements.• Run up two copies of the application without debugging (Select the Debug | Start WithoutDebugging menu command or press Ctrl+F5). In one application customize the interface bymodifying the background color and font (selected off the Options menu), and select theConfiguration | Save menu command.After a period of time (up to 15 seconds) the second application will detect the configuration filehas changed and reload the settings.Task 2 – More information• For more information on how to manage configuration in large application development projects,including extending the configuration and handling deployment issues, see the "AdvancedMicrosoft® .NET Application Deployment Techniques" course which is part of the MasteringIndustrial Strength .NET series. This uses <strong>Enterprise</strong> <strong>Library</strong> to support centralized management ofboth user configuration and system configuration data, with auto-updating applications based on theUpdater Application Block v2.• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp01\Source\vb\exercises\ex03\end\EnoughPI.sln]Lab SummaryIn this lab you performed the following exercises.• Writing custom settings to a configuration file• Reading custom settings from a Configuration File• Configuration File WatcherPage 151


Lab 2 VB.NET Data Access BlockThis lab demonstrates the use of the <strong>Enterprise</strong> <strong>Library</strong> Data Access Block.Lab ObjectiveTime to complete: 30 minutesThe objectives of this lab are: query a database using encrypted configuration settings; use the dataaccess block to read and update a database.• Dynamic SQL with the Data Access Block• Stored Procedures and Updates with the Data Access Block• Encrypting Connection InformationExercise 1: Dynamic SQL with the Data Access BlockThis exercise demonstrates how to do basic database access using the Data Access Block from the<strong>Enterprise</strong> <strong>Library</strong>. It will also show how to configure the block, providing runtime database selectionaccessed via an alias in code.Task 1 – Create the QuickStarts Database• Open the SimpleData [C:\Microsoft Hands-On-Lab\HOLpnp02\Source\vb\exercises\ex01\begin\SimpleData.sln]file, and build the solution.• As part of the installation of <strong>Enterprise</strong> <strong>Library</strong>, there is a batch file to set up the Quick Startsdatabase. Unfortunately, the deployed .sql file is in ANSI, while OSQL uses the OEM code page. Ifyou run the script, you may find that the data entered into the database has accents distorted. To fixthis problem, you need to re-save the file as UTF16, and then re-run the batch file.• Open the file DataAccessQuickStarts.sql in Notepad, which can be found in the [<strong>Enterprise</strong> InstallDir]\QuickStarts\Data [C:\Program Files\Microsoft <strong>Enterprise</strong> <strong>Library</strong>\QuickStarts\Data] directory.Choose File | Save As. Select Unicode as the encoding.Page 152


• Click Save, and click Yes to overwrite the file.Figure 1.1Save as Unicode• Run the batch file SetUpQuickStartsDB.bat, from the same directory.Task 2 – Review the application• Open the MainForm.vb file in the designer. This application contains a DataGrid and some menus,which have no event handlers yet. You will implement counting the available customers in thedatabase, and then loading the customers into the datagrid, using the data access block.Task 3 – Implement the Customer menu items• Double click on the Customers | Count menu item in the designer.Note: This will create an empty event handler for the menu item click event. Within this method, youwant to query the database to find out how many customers are available.• Select the CustomerManagment project. Select the Project | Add Reference menu command.Select the Browse button and select the following assemblies located here [C:\ProgramFiles\Microsoft <strong>Enterprise</strong> <strong>Library</strong>\bin]:Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Data.dll,Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Configuration.dll.Note: You have to add the reference to the Configuration assembly, because most of the providersAPIs within <strong>Enterprise</strong> <strong>Library</strong> derive from either ConfigurationProvider, for abstract classes, orIConfigurationProvider, for interfaces, which are both contained within the Configuration assembly.• Add the following namespace inclusion at the top of the file (before Public Class MainForm):Page 153


Imports Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Data• Add the code below to the Count menu item handler (inserted code in bold):Private Sub mnuCount_Click(ByVal sender As System.Object, ByVal e AsSystem.EventArgs) Handles mnuCount.ClickDim db As Database = DatabaseFactory.CreateDatabase("QuickStartsInstance")Dim count As Integer = CType(db.ExecuteScalar(CommandType.Text,"SELECT COUNT(*) FROM Customers"), Integer)MessageBox.Show("There are " + count.ToString() + " customers in thedatabase")End SubNote: The code above first obtains an <strong>Enterprise</strong> <strong>Library</strong> database instance using the data accessconfiguration for the database instance name "QuickStarts Instance" within the configurationfile. Note that the real database connection is not opened at this point.The db.ExecuteScalarcommand has multiple overloads. The selected overload allows specifying some literal SQL toexecute, and returns the result in a similar manner to the SqlCommand .ExecuteScalar method.Thedb.ExecuteScalar method call is responsible for opening and closing the connection to the realdatabase defined in the configuration file, as well as performing any instrumentation required.• Double click on MainForm.vb in the solution explorer to re-open the form in design view.• Double click on the Customers | Load menu item, to add an empty menu click event handler, andinsert the following code:Private Sub mnuLoad_Click(ByVal sender As System.Object, ByVal e AsSystem.EventArgs) Handles mnuLoad.ClickDim db As Database = DatabaseFactory.CreateDatabase("QuickStartsInstance")Dim ds As DataSet = db.ExecuteDataSet(CommandType.Text, "SELECT *From Customers")DataGrid1.DataSource = ds.Tables(0)End SubNote: The db.ExecuteDataSet method is responsible for opening and closing the connection, aswell as returning a new dataset filled with the results of the SQL Query, which may include multipletables.Task 4 – Configure the application• Add a new Application configuration file (App.config) to the CustomerManagement project. Click onthe CustomerManagement project. Select the File | Add New Item menu command. SelectApplication configuration file template. Leave the Name as App.config.Page 154


Figure 1.2Add New Item• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.• Select File | Open Application and browse to the App.config file located here [C:\Microsoft Hands-On-Lab\HOL-pnp02\Source\vb\exercises\ex01\begin\CustomerManagement].• Right click on the Application and select New | Data Access Application Block.Page 155


Figure 1.3New Data Access Application Block• Select the node Application | Data Access Application Block | Connection Strings | Sql ConnectionString | database. Change the Value property on the right hand side to EntLibQuickStarts.Page 156


Figure 1.4Select the NodeNote: These properties are the components of the connection string that will be used to connect byany database instance that references the connection string node Sql Connection String.• Similarly, select the server node in the tree, and set its Value to (local).• Select the Database Instances | Database Instance node in the tree, and change the Nameproperty to QuickStarts Instance.Page 157


Figure 1.5Set Node NameNote: This name has to match the name you used in the code. This is how you can create an aliasin code that maps to the concrete database instance you wish to use at runtime.• Save the application configuration by choosing File | Save All. Close the <strong>Enterprise</strong> <strong>Library</strong>Configuration Console.Task 5 – Adding a Post-Build Step to a VB.NET Project• The configuration settings files are expected to be found in the same directory as the applicationconfiguration file. Therefore you need to copy the configurations file to the bin directory beforerunning the application. You could copy the file manually, but it would be preferable to automatethe copy as part of the build process.Visual Basic .NET does not provide support for pre and post build events, but you can achieve thesame result by adding a C# class library.If you do not have C# installed, you can accomplish a similar result by using the PrePostBuildRulesAdd-in from Visual Studio .NET 2003 Automation Samples. See these tips for other alternatives.Select the File | Add Project | New Project menu command.• In the New Project dialog select a C# Class <strong>Library</strong> Project, name it Post-Build and click OK.Page 158


Note:• Right click on the new Post-Build project and select Properties from the pop-up menu.• In the project properties dialog select Build Events under Common Properties and set the Post-buildevent command line property to the value below.copy "$(SolutionDir)\CustomerManagement\*.config""$(SolutionDir)\CustomerManagement\bin" > NulFigure 1.6Post Build Property PagesNote: Redirecting the output to the Nul device will prevent the message 1 file(s) copied fromappearing in your build output.• Select the Build | Build Solution menu command. The last thing you will see in the build output isthe message:Performing Post-Build Event. If the post-build event fails for some reason, this will be shown as abuild error.Note: Every time you build the application the post build event will run, copying all the config files inthe CustomerManagementdirectory to the bin directory.Task 6 – Run the application• Run the application. Click the Customers | Count menu to view the number of customers in thedatabase, and use the Customers | Load menu to fill the Data Grid.• Close the application.• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp02\Source\vb\exercises\ex01\end\SimpleData.sln]Page 159


Exercise 2: Stored Procedures and Updates with the Data Access BlockThis exercise demonstrates using the data access block to wrap stored procedure access, as well asperforming updates using a strongly typed DataSet.Task 1 – Add the Categories table to the QuickStarts database• Open the DataEx2 [C:\Microsoft Hands-On-Lab\HOLpnp02\Source\vb\exercises\ex02\begin\DataEx2.sln]file, and build the solution.• Run the batch file SetUpEx02.bat, which can be found in the lab directory:labs\vb\Data Access\exercises\ex02\DbSetup [C:\Microsoft Hands-On-Lab\HOLpnp02\Source\vb\exercises\ex02\DbSetup].Note: This adds a set of categories which you can use to filter the set of products you retrieve andmanipulate.Task 2 – Review the application• Open the MainForm.vb file in the designer. This application will allow us to select a particularcategory, load the products for that category, and then save any changes you make.Task 3 – Implement database retrieval• Right click over the form, and select View Code.• Add the following imports statement to the top of the form code before Public Class MainForm. Youhave already added the required references to the Data and Configuration assemblies.Imports Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Data• Add the following private field to the form, as you'll re-use this database instance in multiple places.Private _db as Database = DatabaseFactory.CreateDatabase("QuickStartsInstance")Note: Note that you can hold onto this, as the Database instance does not represent an openconnection, but instead represents a reference to a database. If you had a SqlConnection objectinstead, then you would not be complying with the Aquire Late, Release Early model.• Find the MainForm_Load method, and insert the following code to obtain the set of categories usinga DataReader:Private Sub MainForm_Load(ByVal sender As Object, ByVal e AsSystem.EventArgs) Handles MyBase.LoadDim dr As IDataReader = NothingTrydr = _db.ExecuteReader("GetCategories")While dr.ReadcmbCategory.Items.Add(New Category(dr.GetInt32(0),dr.GetString(1), dr.GetString(2)))End WhilePage 160


FinallyIf Not dr Is Nothing Then dr.Dispose()End TryEnd SubNote: One of the overloads of the Database.ExecuteReader method takes a string, and an optionalset of parameters. This overload will locate the stored procedure given by the string, read its metadata(and cache it for future use), and set parameters with the values of any argumentsprovided.Note that you are not doing any connection management here, but it is very important toDispose the data reader returned. This is accomplished by the try/finally block in the code above.When the data reader is disposed, the underlying DbConnection is also closed.• Find the cmbCategory_SelectedIndexChanged method and insert the following code, which willread a subset of the products, based on the selected category drop down.Private Sub cmbCategory_SelectedIndexChanged(ByVal sender AsSystem.Object, ByVal e As System.EventArgs) HandlescmbCategory.SelectedIndexChangeddsProducts.Clear()Dim selectedCategory As Category = CType(cmbCategory.SelectedItem,Category)If selectedCategory Is Nothing Then Return_db.LoadDataSet("GetProductsByCategory", dsProducts, _New String() {"Products"}, selectedCategory.CategoryId)End SubNote: There are two methods on the Database class that populate a DataSet; ExecuteDataSet andLoadDataSet. ExecuteDataSet returns a newly created DataSet, while LoadDataSet populates anexisting one.The overload used here takes a stored procedure as the first argument, the dataset topopulate, a set of tables to map the result of the procedure into, and an arbitrary number ofarguments. The additional arguments are mapped to any stored procedure arguments retrievedfrom the database metadata.Task 4 – Implement Updating the Database• Find the btnSave_Click method. Insert the following code, which will update the database based onany changes in the dataset.Private Sub btnSave_Click(ByVal sender As System.Object, ByVal e AsSystem.EventArgs) Handles btnSave.ClickDim insertCommandWrapper As DBCommandWrapper =_db.GetStoredProcCommandWrapper("AddProduct")insertCommandWrapper.AddInParameter("@ProductName", DbType.String,"ProductName", DataRowVersion.Current)insertCommandWrapper.AddInParameter("@CategoryID", DbType.Int32,"CategoryID", DataRowVersion.Current)insertCommandWrapper.AddInParameter("@UnitPrice", DbType.Currency,"UnitPrice", DataRowVersion.Current)Dim deleteCommandWrapper As DBCommandWrapper =_db.GetStoredProcCommandWrapper("DeleteProduct")deleteCommandWrapper.AddInParameter("@ProductID", DbType.Int32,"ProductID", DataRowVersion.Original)deleteCommandWrapper.AddInParameter("@LastUpdate", DbType.DateTime,Page 161


"LastUpdate", DataRowVersion.Original)Dim updateCommandWrapper As DBCommandWrapper =_db.GetStoredProcCommandWrapper("UpdateProduct")updateCommandWrapper.AddInParameter("@ProductID", DbType.Int32,"ProductID", DataRowVersion.Original)updateCommandWrapper.AddInParameter("@ProductName", DbType.String,"ProductName", DataRowVersion.Current)updateCommandWrapper.AddInParameter("@CategoryID", DbType.Int32,"CategoryID", DataRowVersion.Current)updateCommandWrapper.AddInParameter("@UnitPrice", DbType.Currency,"UnitPrice", DataRowVersion.Current)updateCommandWrapper.AddInParameter("@LastUpdate", DbType.DateTime,"LastUpdate", DataRowVersion.Original)DataRowVersion.Current)_db.UpdateDataSet(dsProducts, "Products", _insertCommandWrapper, updateCommandWrapper,deleteCommandWrapper, _UpdateBehavior.Standard)End SubNote: When updating a database, it is required to manually create the wrappers around the storedprocedures, as it needs to know the mapping between the columns in the DataTable and the storedprocedure arguments.With this overload of the UpdateDataSet method, it is possible to get the DataAccess Block to execute all the updates transactionally, by setting the UpdateBehaviour toTransactional. For more information, see the <strong>Enterprise</strong> <strong>Library</strong> documentation.Task 5 – Run the Application• You have pre-created the application configuration file and data access configuration file, exactlythe same as in the previous exercise, and added the Post-Build project.Run the application, select a category from the drop down, and see how it loads and saves thedata.Task 6 – More information• For more information on designing and implementing data access layers, and knowing whether tocreate strongly-typed data accesss layers (via code generation) vs weakly typed data access layer(e.g. the data access block) see the "Developing Microsoft® .NET Applications Using CodeGeneration, UI Process and Abstraction" course which is part of the Mastering Industrial Strength.NET series. This uses <strong>Enterprise</strong> <strong>Library</strong> as one of the options for abstracting data access, as wellas implementing code generation to generate transaction aware data access layers.• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp02\Source\vb\exercises\ex02\end\DataEx2.sln]Exercise 3: Encrypting Connection InformationPage 162


In this exercise, you will encrypt the configuration to prevent connection string discovery by someonecopying the application configuration file from the machine.Task 1 – Encrypt the database configuration• Open the DataEx3 [C:\Microsoft Hands-On-Lab\HOLpnp02\Source\vb\exercises\ex03\begin\DataEx3.sln]file, and build the solution.• Make sure any currently open Configuration Console instances are closed.• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.• Select File | Open Application and browse to the App.configfile located here [C:\Microsoft Hands-On-Lab\HOL-pnp02\Source\vb\exercises\ex03\begin\ProductMaintenance].• Right click over the Encryption Settings node, and add a new File Key Algorithm Storage Provider.Figure 3.1Add New File Key Algorithm Storage ProviderNote: The File Key Algorithm storage provider uses an external file to contain the key used toencrypt and de-crypt the configuration information. This potentially allows multiple applications toshare the same key stored with a single file, which may itself be machine or user encrypted.• Select Create a new key algorithm pair. Click Next.Page 163


Figure 3.2Create New Key Algorithm pair• Click Select Algorithm. Choose the RijndaelManaged symmetric encryption algorithm. Click Next.Figure 3.3Select AlgorithmNote: This selects the algorithm that will be used to encrypt and decrypt the configuration data.• Click on the Generate button, which will create a random key that is valid for the selected algorithm.Click Next.Page 164


Figure 3.4Generate Random Key• Click Select File. Type in the filename Configuration.key. Click Save.Note: This is the file that will contain the encryption key. Note: This will be saved as an absolutepath name, which will need to be updated if the application is deployed with the key file.• Check the Enable DPAPI checkbox. Select the Machine radio button.Figure 3.5Enable DPAPI protectionNote: This will encrypt the key file using the Data Protection API, which is built into Windows. Thismeans that the key file can only be read on this machine. If you had selected the User option, thenthe key file would only have been readable on this machine by this user (if roaming profiles are notenabled for the user), or on any machine within the domain for the user (if roaming profiles areenabled).Note that if you want to deploy a key file to another machine or user, and want to useDPAPI encryption on the target machine, then you will need to deploy it un-encrypted, and encryptit on the machine at install time (or post install).• Click Finish.• Select the dataConfiguration node of the configuration. Change the Encrypt property to True.• Save the configuration by selecting File | Save All.• Notes:Because the configuration references an external key file, this allows the configuration to bedeployed in an encrypted format. At deployment time, the external key file needs to be encryptedusing DPAPI for either the specific machine, or the specific user. This means that the only machineor user dependant modification for the encryption is the single key file, rather than having to reencryptall the configuration. This allows the configuration to be stored centrally in an encryptedform, encrypted with the shared key that all the clients have, but each client's key is itself encryptedwith DPAPI. This means that if the key is compromised on one machine, however, then theconfiguration can be tampered with on all the machines.Another issue to be careful of is, the path to the key file has to be an absolute path, as the currentbuild of the configuration run-time will resolve any relative paths relative to the working directory. Inthe case of the configuration console, the working directory is initially the console executabledirectory, whereas for the applications, the working directory is the application directory.Page 165


Task 2 – Review the encrypted configuration• Open the app.config. Note the new keyAlgorithmStorageProvider element.• Open the dataconfiguration.config. You will notice that it is now a binary, encrypted file.Task 3 – Run the application• Run the application and note that it behaves as in the previous exercise.Lab SummaryIn this lab you performed the following exercises.• Dynamic SQL with the Data Access Block• Stored Procedures and Updates with the Data Access Block• Encrypting Connection InformationPage 166


Lab 3 VB.NET Caching BlockThis lab demonstrates the use of the <strong>Enterprise</strong> <strong>Library</strong> Caching Block. It requires SQL Serveror MSDE pre-installed.Lab ObjectiveTime to complete: 30 minutesThe objectives of this lab are: add persistent caching to a windows application; use background loadingto populate a cache.• Using the Caching Block for performance• Persistent Caching and EncryptionExercise 1: Using the Caching Block for performanceTask 1 – Populate the QuickStarts database with employee data• Open the CachingEx01 [C:\Microsoft Hands-On-Lab\HOLpnp03\Source\vb\exercises\ex01\begin\CachingEx01.sln]file, and build the solution.• Run the batch file SetCachingHOL.bat, which can be found in the lab directory:labs\vb\Caching\setup [C:\Microsoft Hands-On-Lab\HOL-pnp03\Source\vb\exercises\].Note: This adds a set of employees which you will use to create an employee directory.Task 2 – Review the Application• Open the MainForm.vb file in the designer.Note: This application is a browser for the employee contact details stored in the database. As partof the browser the application displays a photograph of the employee.• Right click over the form, and select View Code. Find the method MainForm_Load. The Form usesa class called EmployeeService to obtain the data to display. This in turn uses theEmployeeDataProvider class, as displayed in the picture below:Page 167


Figure 1.1Note: Currently, the EmployeeService just delegates directly to the EmployeeDataProviderclass. You will enhance this class to use the Caching Block and to react appropriately when theapplication is offline.• Right click over the EmployeeDataProvider.vb file, and select View Code. Find the methodGetEmployeePhotoData. Notice that you have added a delay of 1 second into the method, tosimulate slow access to a database.• Run the application. Browse through the employees. Notice that there is a significant delay whilebrowsing through photos, even when you have viewed a photo previously.Task 3 – Implement caching in the EmployeeService class• Select the EmployeeBrowser project. Select the Project | Add Reference menu command. Selectthe Browse button and select the following assembly located here [C:\Program Files\Microsoft<strong>Enterprise</strong> <strong>Library</strong>\bin]:Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Caching.dll.• Double click over the EmployeeService.vb file in the Solution Explorer.• Add the following namespace inclusion to the list of namespaces at the top of the file:Imports Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Caching• Replace the code in GetEmployeePhoto with the code below (modified code in bold):Public Function GetEmployeePhoto(ByVal employeeId As Guid) As BitmapDim cache As CacheManager = CacheFactory.GetCacheManager()Dim photoData As Byte() = CType(cache(employeeId.ToString()),Byte())If photoData Is Nothing ThenDim dataProvider As New EmployeeDataProviderphotoData = dataProvider.GetEmployeePhotoData(employeeId)cache.Add(employeeId.ToString(), photoData)End IfIf photoData Is Nothing ThenReturn NothingEnd IfReturn New Bitmap(New MemoryStream(photoData))Page 168


End Function 'GetEmployeePhotoNote: This method uses the factory model, as with the rest of <strong>Enterprise</strong> <strong>Library</strong>, to create a newCacheManager instance. This can be purely in-memory, or can be backed by a physical storagemedium, depending on configuration.Items can be retrieved from the cache by using an indexer,and can be added (or replaced) by using the Add method. The overload used in this method doesnot specify an expiration policy.• Add a new method to the EmployeeService class, to allow the form to request the service to getnew data:Public Shared Sub ClearCache()Dim cache As CacheManager = CacheFactory.GetCacheManager()cache.Flush()End Sub 'ClearCacheNote: This method will remove all items from the cache.• Double click on MainForm.vb in the solution explorer to re-open the form in design view.• Double click on the File | Clear Cache menu item, to add an empty menu click event handler, andinsert the following code:Private Sub mnuClearCache_Click(ByVal sender As Object, ByVal e AsSystem.EventArgs) Handles mnuClearCache.ClickEmployeeService.ClearCache()End Sub 'mnuClearCache_ClickTask 4 – Configure the application• In order to configure the caching block, you require an application configuration file. There isalready one added to this project, which is used for the connection string for the database. You nowneed to add the caching configuration section to it.• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.• Select File | Open Application and browse to the App.configfile located here [C:\Microsoft Hands-On-Lab\HOL-pnp03\Source\vb\exercises\ex01\begin\EmployeeBrowser].• Right click on the Application and select New | Caching Application Block.Page 169


Figure 1.2Caching Application Block• Select the node Application | Caching Application Block | Cache Managers | Cache Manager. Heresome of the settings you can change to tune the performance of our cache. For now, leave thesettings as they are.Page 170


Figure 1.3Caching Application Block• Save the application configuration by choosing File | Save All. Close the <strong>Enterprise</strong> <strong>Library</strong>Configuration Console.Task 5 – Adding a Post-Build step to a VB.NET Project• The configuration settings file is expected to be found in the same directory as the applicationconfiguration file. Therefore you need to copy the caching access block configuration file to the bin[C:\Microsoft Hands-On-Lab\HOLpnp03\Source\vb\exercises\ex01\begin\EmployeeBrowser\bin]directory before running theapplication. You could copy the file manually, but it would be preferable to automate the copy aspart of the build process.VB.NET does not provide support for pre and post build events as C# does, but the same thing canbe accomplished by adding a utility makefile from C++. It is simple to do and does not require anyC++ coding.Select the File | Add Project | New Project menu command.• In the New Project dialog expand the Visual C++ Projects node in the project types list and selectGeneral. In the templates list select Makefile Project. Name the project Post-Build and click Ok.Page 171


Figure 1.4Create a new Configuration Application• You will then see the Makefile wizard, just click Finish to dismiss the wizard and create yourmakefile project.• Right click on the new Post-Build makefile project and select Properties from the popup menu.• In the project properties dialog select General under Configuration Properties and set theConfiguration Type to Utility.Figure 1.5Configuration TypeNote: Note: You will need to click Ok twice to dismiss the project properties dialog.• Right click on the Post-Build makefile project and select Properties from the popup menu.• Select Configuration Properties / Build Events / Post-Build Event and enter the following commandline.Page 172


copy "$(SolutionDir)\EmployeeBrowser\*.config""$(SolutionDir)\EmployeeBrowser\bin" > NulNote: Redirecting the output to the Nul device will prevent the message 1 file(s) copied fromappearing in your build output.• Select the Build | Build Solution menu command. The last thing you will see in the build output isthe message below.If the post-build event fails for some reason, this will be shown as a build error.Performing Post-Build Event.Task 6 – Run the application• Run the application. As you browse through notice the performance difference for cached photos.• Close the application and Close Visual Studio .NET.• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp03\Source\vb\exercises\ex01\end\CachingEx01.sln]Exercise 2: Persistent Caching and EncryptionThis exercise demonstrates using expiration policies, persistent backing stores, and backgroundloading of an offline cache.Task 1 – Add caching to the initial load of the employee contact details• Open the CachingEx02 [C:\Microsoft Hands-On-Lab\HOLpnp03\Source\vb\exercises\ex02\begin\CachingEx02.sln]file, and build the solution.• Instructions.Task 2 – Implement offline caching• Open the EmployeeServices.vb file. Add the following import statement to the top of the file:Imports Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Caching.Expirations• Find the method GetContactDetails, and replace the code with:Public Function GetContactDetails() As EmployeesDataSetDim cache As CacheManager = CacheFactory.GetCacheManager()Dim dsEmployees As EmployeesDataSet =CType(cache("EmployeeContacts"), EmployeesDataSet)If dsEmployees Is Nothing ThenIf ConnectionManager.IsOnline ThendsEmployees = LoadContactDetails()ElsedsEmployees = New EmployeesDataSetEnd IfPage 173


End IfReturn dsEmployeesEnd Function 'GetContactDetailsNote: This code will only attempt to contact the database when the application is online.• Add a new method LoadContactDetails which contains the logic to call the data provider, and addthe returned data to the cache:Private Function LoadContactDetails() As EmployeesDataSetDim cache As CacheManager = CacheFactory.GetCacheManager()Dim dataProvider As New EmployeeDataProviderDim dsEmployees As EmployeesDataSet = dataProvider.GetEmployees()' Expire in 2 dayscache.Add("EmployeeContacts", dsEmployees, CacheItemPriority.High,Nothing, New ICacheItemExpiration() {New AbsoluteTime(New TimeSpan(2, 0,0, 0))})Return dsEmployeesEnd Function 'LoadContactDetailsNote: This overload of the Add method allows specifying cache item priority, a cache item removalcallback (which must be serializable for persistent caches), and a set of expiration policies. In thiscase, you do not want to allow our users to keep the employee data on their machines for morethan 2 days without checking in. This helps to reduce the possibility of an employee taking thecontact data to another company, as the caching infrastructure will remove the contents once theyhave expired.• Modify the GetEmployeePhoto method to not attempt to retrieve information from the databasewhen offline (modified code in bold):Public Function GetEmployeePhoto(ByVal employeeId As Guid) As BitmapDim cache As CacheManager = CacheFactory.GetCacheManager()Dim photoData As Byte() = CType(cache(employeeId.ToString()),Byte())If photoData Is Nothing AndAlso ConnectionManager.IsOnline ThenDim dataProvider As New EmployeeDataProviderphotoData = dataProvider.GetEmployeePhotoData(employeeId)cache.Add(employeeId.ToString(), photoData)End IfIf photoData Is Nothing ThenReturn NothingEnd IfReturn New Bitmap(New MemoryStream(photoData))End Function 'GetEmployeePhotoTask 3 – Configure the persistent cache• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.Page 174


• Select File | Open Application and browse to the App.config file located here [C:\Microsoft Hands-On-Lab\HOL-pnp03\Source\vb\exercises\ex02\begin\EmployeeBrowser].• Right click over the node Application | Caching Application Block | Cache Managers | CacheManager. Select New -> Isolated Storage.Figure 2.1Create a new Isolated Storage• Change the Partition Name property to be:EmployeeBrowserNote: The Partition Name allows multiple caches to share the same Isolated storage location.• Save the configuration by selecting the menu File | Save All. Close the configuration console.Task 4 – Run the application• Swap back to Visual Studio.NET, and run the application, but only browse to a few of theemployees, to load the cache with data.• Close the application, and open the file ConnectionManager.vb. Change the IsOnline property toreturn False, to simulate the application being started offline.Public Shared ReadOnly Property IsOnline() As BooleanGetReturn FalseEnd GetEnd PropertyPage 175


Note: Normally this class would be responsible for pinging a server to see whether the client isonline or not. See the offline application block for ways to do this.• Re-run the application. This time, you should see that some of the employees do not have theirphoto in the cache. The next task will be to automatically pre-load the cache in the backgroundwhen online.• Close the application. Change the IsOnline property back to True:Public Shared ReadOnly Property IsOnline() As BooleanGetReturn TrueEnd GetEnd PropertyTask 5 – Implement background pre-loading of the cache when online• Open the EmployeeService.vb file. Add the following two methods, which will load the cache in thebackground:Private Sub PopulateCache()Dim cache As CacheManager = CacheFactory.GetCacheManager()Dim dsEmployees As EmployeesDataSet = LoadContactDetails()Dim employee As EmployeesDataSet.EmployeesRowFor Each employee In dsEmployees.EmployeesIf Not cache.Contains(employee.EmployeeID.ToString()) ThenDim dataProvider As New EmployeeDataProvidercache.Add(employee.EmployeeID.ToString(),dataProvider.GetEmployeePhotoData(employee.EmployeeID))End IfNext employeeEnd Sub 'PopulateCacheDelegate Sub PopulateCacheDelegate()Public Sub BeginBackgroundLoad()If Not ConnectionManager.IsOnline ThenReturnEnd IfDim mi As New PopulateCacheDelegate(AddressOf PopulateCache)mi.BeginInvoke(Nothing, Nothing)End Sub 'BeginBackgroundLoadNote: The BeginBackgroundLoad method uses a delegate to begin the PopulateCache method ona background thread, handled by the .NET worker thread implementation.The Caching Blockguarantees us thread safety when using the Cache, so it is safe to access from multiple threads atthe same time.The PopulateCache method gets an updated set of employee data from thedatabase, by calling LoadContactDetails, which also stores the new set of data in the cache. It theniterates through all the rows in the Employees table. Note that this is actually not a safe activity ifthe user can add or remove rows in the dataset on another thread (instead it would be better toSelect the set of rows, and then iterate through them).• Right click over the MainForm.vb file, and select View Code. Find the MainForm_Load method, andadd the following line at the end (inserted code in bold):Page 176


Private Sub MainForm_Load(ByVal sender As Object, ByVal e AsSystem.EventArgs) Handles MyBase.LoadDim service As New EmployeeServiceDim tempDataset As EmployeesDataSet = service.GetContactDetails()dsEmployees.Merge(tempDataset)service.BeginBackgroundLoad()End Sub 'MainForm_LoadTask 6 – Run the application• Run the application, and wait about 10 seconds. Shutdown the application, and change the IsOnlineproperty within the ConnectionManager.vb file to be false:Public Shared ReadOnly Property IsOnline() As BooleanGetReturn FalseEnd GetEnd Property• Start the application again, and this time all the photos should be available while "offline".Task 7 – More information• For more information on how to use caching in building occasionally connected solutions, seethe "Extending Business Workflow to Mobile Devices By Using Microsoft® .NET"course which ispart of the Mastering Industrial Strength .NET series. This uses <strong>Enterprise</strong> <strong>Library</strong> to supportcaching of reference data and Web service requests, using both intrusive application code changesand proxy interception based offline handling techniques.• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp03\Source\vb\exercises\ex02\end\CachingEx02.sln]Lab SummaryIn this lab you performed the following exercises.• Using the Caching Block for performance• Persistent Caching and EncryptionPage 177


Lab 4 VB.NET <strong>Enterprise</strong> <strong>Library</strong> Logging Hands On LabThis lab demonstrates the use of the <strong>Enterprise</strong> <strong>Library</strong> Logging and Instrumentation Application Block.Lab ObjectiveTime to complete: 30 minutesThe objectives of this lab are: use the <strong>Enterprise</strong> <strong>Library</strong> Logging and Instrumentation Application Blockto implement logging in an application; configure log sinks and use custom log sinks.• Add Logging to an Application• Create and use a custom log sink• Create and use a custom log formatterExercise 1: Add Logging to an ApplicationIn this exercise you will add logging and tracing to an existing application. You will configure log sinksvia the <strong>Enterprise</strong> <strong>Library</strong> Configuration Tool.Task 1 – <strong>Enterprise</strong> <strong>Library</strong> Configuration Tool• Open the EnoughPI [C:\Microsoft Hands-On-Lab\HOLpnp04\Source\vb\exercises\ex01\begin\EnoughPI.sln]file, and build the solution.• Select the Debug | Start menu command to run the application.The EnoughPI application calculates the digits of pi (p, the ratio of the circumference of a circle toits diameter). Enter your desired precision via the NumericUpDown control and click the Calculatebutton. Be prepared to wait if you want more than 500 digits of precision.You can use the <strong>Enterprise</strong> <strong>Library</strong> Logging Application Block to log application state.Page 178


Figure 1.1Never Enough PI• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.• Select File | Open Application and browse to the App.configfile located here [C:\Microsoft Hands-On-Lab\HOL-pnp04\Source\vb\exercises\ex01\begin\EnoughPI].• Right click on the Application and select New | Logging and Instrumentation Application Block.Figure 1.2Create a new Logging and Instrumentation Application BlockPage 179


Note: A Logging and Instrumentation Application Block section is created to set the loggingconfiguration information. A Configuration Application Blocksection is created to set the loggingconfiguration file information.• The default Logging and Instrumentation Application Block Client Settings define an in-processdistribution strategy and specify that logging is enabled.Figure 1.3Create a new Category• The default Logging and Instrumentation Application Block Distributor Settings define twoCategories (General, and Trace). Categories are text tags that you may apply to your log events togroup them. The General category defines one destination, Event Log Destination, which pairs theEvent Log Sink with the Text Formatter. The Trace category defines on destination, Flat FileDestination, which pairs the Flat File Sink with the Text Formatter.New categories may be added by right clicking on Categories and selecting New | Category. Acategory may have many destinations. A destination defines a log formatter and log sink pair.Each formatter and sink may be used in many destinations.Page 180


Figure 1.4Create a new Category• Select the File | Save All menu command to save the configuration.• Select the EnoughPI project in Visual Studio. Select the Project | Show All Files menu command.Note two logging configuration files (loggingconfiguration.config andloggingdistributorconfiguration.config) have been created.Note: The loggingconfiguration.config file contains the Client Settings (incl. the DistributionStrategies). The loggingdistributorconfiguration.config file contains the Distributor Settings (incl. theCategories, sinks and Formatters).Task 2 – Adding a Post-Build step to a VB.NET Project• The configuration settings file is expected to be found in the same directory as the applicationconfiguration file. Therefore we need to copy the EnoughPISettings.config configuration file to thebin directory before running the application. We could copy the file manually, but it would bepreferable to automate the copy as part of the build process.VB.NET does not provide support for pre and post build events, but we can achieve the same resultby adding a C# class library.If you do not have C# installed, you can accomplish a similar effect by using the PrePostBuildRulesAdd-in you can get from Visual Studio .NET 2003 Automation Samples. See these tips for otheralternatives.Page 181


Select the File | Add Project | New Project menu command.• In the New Project dialog select a C# Class <strong>Library</strong> Project, name it Post-Build and click OK.Note:Figure 1.5• Right click on the new Post-Build project and select Properties... from the popup menu.• In the project properties dialog select Build Events under Common Properties and set the Post-buildevent command line property to the value below.copy "$(SolutionDir)\EnoughPI\*.config" "$(SolutionDir)\EnoughPI\bin" >NulFigure 1.6Page 182


Note: Redirecting the output to the Nul device will prevent the message 1 file(s) copied fromappearing in your build output.• Select the Build | Build Solution menu command. The last thing you will see in the build output isthe message:Performing Post-Build Event...If the post-build event fails for some reason, this will be shown as a build error.Note: Every time you build the application the post build event will run, copying all the config files inthe EnoughPI directory to the bin directory.Task 3 – Adding Logging• In Visual Studio select the EnoughPI project. Select the Project | Add Reference … menucommand. Select the Browse button and select the following assemblies located here [C:\ProgramFiles\Microsoft <strong>Enterprise</strong> <strong>Library</strong>\bin].Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Logging.dll(Contains the logging application block base classes).• In Visual Studio, open the Calculator.vb file in the Calc folder of the EnoughPI project, and add thefollowing namespace.Imports Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Logging• Log the calculation completion by adding the following code to the OnCalculated method in theCalculator.vbfile. Create a new LogEntry. Set the desired log entry parameters, and use theLogger to write the log entry to the log Distributor (to send formatted log entries to the categorydestination).Protected Sub OnCalculated(ByVal args As CalculatedEventArgs)' TODO: Log final resultDim log As LogEntry = New LogEntrylog.Message = String.Format("Calculated PI to {0} digits",args.Digits)log.Category = Category.Generallog.Priority = Priority.NormalEnd SubLogger.Write(log)RaiseEvent Calculated(Me, args)Note: Notes: You have used constants for the Category and Priority rather than use hard-codedtags and integers(defined in Constants.vb in the EnoughPI.Logging project).• Log the calculation progress by adding the following code to the OnCalculating method in theCalculator.vbfile.Page 183


Protected Sub OnCalculating(ByVal args As CalculatingEventArgs)' TODO: Log progressLogger.Write( _String.Format("Calculating next 9 digits from {0}",args.StartingAt), _Category.General, _Priority.Low _)RaiseEvent Calculating(Me, args)If (args.Cancel = True) Then' TODO: Log cancellationLogger.Write("Calculation cancelled by user!", Category.General,Priority.High)End IfEnd SubNote: Notes: You have used an overload of the Write method on the Logger class to shortcutcreating a LogEntry.• Log calculation exceptions by adding the following code to the OnCalculatorException method inthe Calculator.vbfile.Protected Sub OnCalculatorException(ByVal args AsCalculatorExceptionEventArgs)End Sub' TODO: Log exceptionIf Not (TypeOf (args.Exception) Is ConfigurationException) ThenLogger.Write(args.Exception, Category.General, Priority.High)End IfRaiseEvent CalculatorException(Me, args)Note: Notes: You must test that the exception type is not a ConfigurationException as you wouldnot be able to use the Logger if it has not be correctly configured.You use the <strong>Enterprise</strong> <strong>Library</strong>Exception Handling Application Block to create a consistent strategy for processing exceptions.• Select the Debug | Start menu command to run the application. Enter your desired precision andclick the Calculate button. Log entries will be written to the windows event log.• Run the Event Viewer. From the Windows Start menu select Administrative Tools l Event Viewer.View the Application log for messages from the <strong>Enterprise</strong> <strong>Library</strong> Logging source.Page 184


Figure 1.7Event Viewer• Double click on a log entry to view the formatted log message.Figure 1.8Event PropertiesTask 4 – Adding Tracing• The Logging and Instrumentation Application Block includes tracing which allows us to book-end asection of code and log the execution time.Page 185


• Return to the <strong>Enterprise</strong> <strong>Library</strong> Configuration Tool. If you closed it, re-open by: From the WindowsStart menu select All Programs | Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong><strong>Library</strong> Configuration, and re-open the App.configfile located here [C:\Microsoft Hands-On-Lab\HOL-pnp04\Source\vb\exercises\ex01\begin\EnoughPI].• Set TracingEnabled to True in the Client Settings of the Logging and Instrumentation ApplicationBlock configuration section.Figure 1.9Tracing Enabled• Select the File | Save All menu command to save the configuration.• In Visual Studio, open the Calculator.vb file in the EnoughPI project, and add thefollowing namespace.Imports Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Logging.Tracing• Trace (for timing) the complete calculation by adding the following code to the Calculate method inthe Calculator.vb file of the EnoughPI project.Public Function Calculate(ByVal digits As Integer) As StringDim pi As New StringBuilder("3", digits + 2)Dim result As String = NothingTryIf digits > 0 Then' TODO: Add Tracing around the calculationDim trace As New Tracer(Category.Trace, "Calculate PI")TryPage 186


1)pi.Append(".")Dim i As Integer = 0While (i < digits)Dim args As CalculatingEventArgsargs = New CalculatingEventArgs(pi.ToString(), i +OnCalculating(args)' Break out if cancelledIf args.Cancel = True ThenExit WhileEnd If' Calculate next 9 digitsDim nineDigits As Integer =NineDigitsOfPi.StartingAt((i + 1))Dim digitCount As Integer = Math.Min(digits - i, 9)Dim ds As String = String.Format("{0:D9}",nineDigits)pi.Append(ds.Substring(0, digitCount))End Ifi += 9End WhileFinallytrace.Dispose()End Tryresult = pi.ToString()' Tell the world I've finished!OnCalculated(New CalculatedEventArgs(result))Catch ex As Exception' Tell the world I've crashed!OnCalculatorException(New CalculatorExceptionEventArgs(ex))End TryReturn resultEnd FunctionNote: Why call Dispose() in a Finally block? The Tracer will stop timing, and log its end tracemessage, when it is disposed. You call Dispose() manually on the Tracer so that you control whenthe object is disposed, and not the Garbage Collector. Allowing the Garbage Collector to dispose ofthe Tracer will result in incorrect timings.• Run the application, and perform a calculation. Close the application.• The Trace has been configured to write to the trace.log [C:\Microsoft Hands-On-Lab\HOLpnp04\Source\vb\exercises\ex01\begin\EnoughPI\bin\trace.log]file via the Flat File Sink. View theelapsed time in the end trace message.• Close Visual Studio.• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp04\Source\vb\exercises\ex01\end\EnoughPI.sln]Page 187


Exercise 2: Create and use a custom log sinkTask 1 – Create a custom log sink• Open the EnoughPI [C:\Microsoft Hands-On-Lab\HOLpnp04\Source\vb\exercises\ex02\begin\EnoughPI.sln]file, and build the solution.• In Visual Studio select the EnoughPI.Logging project. Select the Project | Add Reference … menucommand. Select the Browse button and select the following assemblies located here [C:\ProgramFiles\Microsoft <strong>Enterprise</strong> <strong>Library</strong>\bin].Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Configuration.dll(All Blocks depend on the Configuration Application Block for handling configuration files etc. Youmust include this assembly for the configuration of our custom sink).Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Logging.dll(Contains the logging application block base classes).• In Visual Studio, open the ConsoleSink.vb file in the Sinksdirectory of the EnoughPI.Loggingproject. Add the following namespaces.Imports Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.ConfigurationImports Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.LoggingImports Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Logging.SinksImportsMicrosoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Logging.Distributor.Configuration• Add the following code to the ConsoleSink class.Public Class ConsoleSinkInherits LogSinkPrivate configView As LoggingConfigurationViewPublic Overrides Sub Initialize(ByVal configurationView AsConfigurationView)Me.configView = CType(configurationView,LoggingConfigurationView)End SubProtected Overrides Sub SendMessageCore(ByVal entry As LogEntry)Dim sinkData As CustomSinkDatasinkData =CType(Me.configView.GetSinkData(Me.ConfigurationName), _CustomSinkData)' Delimit each log entryConsole.WriteLine(sinkData.Attributes("delimiter"))' Write the formatted log entry to the ConsoleConsole.WriteLine(FormatEntry(entry))Page 188


End SubEnd ClassNote: Note: The base class is LogSink, which mandates that you override two abstract methods(Initialize and SendMessageCore). The Initialize method allows us to get access to theConfigurationView (ie. a view of the configuration parameters as specified in the configurationfiles). The SendMessageCore method is invoked by the logging infrastructure to send the messageto the log sink. The ConsoleSink is expecting a parameter, named delimiter, be defined in theconfiguration of this custom log sink.• Select Build | Build Solution to compile the complete solution.Task 2 – Using a custom sink• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.• Select File | Open Application and browse to the App.configfile located here [C:\Microsoft Hands-On-Lab\HOL-pnp04\Source\vb\exercises\ex02\begin\EnoughPI].• Right click on the Sinks node of the Logging and Instrumentation Application Block DistributorSettings. Select the New | Custom Sink menu command.Figure 2.1Create a new Custom Sync• Select the Name field and rename the Custom Sink as Console Sink.• Select the TypeName field and click the ellipses to display the Type Selector dialog.Page 189


Figure 2.2TypeName• Click the Load an Assembly button and browse to the EnoughPI.Logging.dll assembly here[C:\Microsoft Hands-On-Lab\HOL-pnp04\Source\vb\exercises\ex02\begin\EnoughPI\bin] and clickthe Open button.• Select the ConsoleSink class from the EnoughPI.Logging assembly and click the Ok button.Figure 2.3Console Sync• Select the Attributes field and click the ellipses to display the NameValueItem Collection Editor.Figure 2.4Attributes• Click the Add button to add a new NameValueItem. Set the Name field to delimiter, and the Valueto a line of dashes (eg. ------------------). Click the Ok button.Page 190


Figure 2.5Delimiter PropertiesNote: Note: You will remember our ConsoleSink is expecting a parameter named delimiter, which isprinted before each log entry is written to the console.• Right click on the General category of the Logging and Instrumentation Application Block DistributorSettings. Select the New | Destination menu command.Figure 2.6Create a new Destination• Set the following properties for the destination.Formatter = Text Formatter,Name = Console Destination, andSink = Console Sink.Page 191


• Add a similar destination to the Trace category.Figure 2.7Create a new DestinationFigure 2.8Create a new Destination• Select the File | Save All menu command to save the configuration. Close the ConfigurationConsole.Page 192


Task 3 – View the sink output• In Visual Studio, click on the EnoughPI project. Select the Project | Properties menu command toview the EnoughPI Property Pages. Select Common Properties | General and add change theOutput type to Console Application.Figure 2.9Console Application• In Visual Studio, select the Debug | Start menu command to run the application. Click the Calculatebutton. The log entries will be displayed in the applications Console Window.Page 193


Figure 2.10Never Enough PI• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp04\Source\vb\exercises\ex02\end\EnoughPI.sln]Exercise 3: Create and use a custom log formatterIn this exercise you will add a custom log formatter to a logging application.Task 1 – Create a custom log formatter• Open the EnoughPI [C:\Microsoft Hands-On-Lab\HOLpnp04\Source\vb\exercises\ex03\begin\EnoughPI.sln]file, and build the solution.• In Visual Studio, open the XmlFormatter.vb file in the Formattersdirectory of the EnoughPI.Loggingproject. Add the following namespaces.Imports Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.ConfigurationImports Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.LoggingImportsMicrosoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Logging.Distributor.ConfigurationImports Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Logging.Formatters• Add the following code to the XmlFormatter class.Public Class XmlFormatterInherits ConfigurationProviderImplements ILogFormatterPrivate configView As LoggingConfigurationViewPublic Overrides Sub Initialize(ByVal configurationView AsConfigurationView)Me.configView = CType(configurationView,LoggingConfigurationView)End SubPublic Function Format(ByVal log As LogEntry) As String ImplementsILogFormatter.FormatDim result As String = NothingDim sw As StringWritersw = New StringWriter(CultureInfo.InvariantCulture)TryDim w As XmlTextWriterw = New XmlTextWriter(sw)w.Formatting = Formatting.Indentedw.Indentation = 2w.WriteStartDocument(True)w.WriteStartElement("logEntry")w.WriteAttributeString("Category", log.Category)w.WriteAttributeString("Priority",log.Priority.ToString(CultureInfo.InvariantCulture))Page 194


w.WriteElementString("Timestamp", log.TimeStampString)w.WriteElementString("Message", log.Message)w.WriteElementString("EventId",log.EventId.ToString(CultureInfo.InvariantCulture))w.WriteElementString("Severity",log.Severity.ToString(CultureInfo.InvariantCulture))w.WriteElementString("Title", log.Title)w.WriteElementString("Machine", log.MachineName)w.WriteElementString("AppDomain", log.AppDomainName)w.WriteElementString("ProcessId", log.ProcessId)w.WriteElementString("ProcessName", log.ProcessName)w.WriteElementString("Win32ThreadId", log.Win32ThreadId)w.WriteElementString("ThreadName", log.ManagedThreadName)w.WriteEndElement()w.WriteEndDocument()result = sw.ToString()FinallyIf Not sw Is Nothing Then sw.Close()End TryReturn resultEnd FunctionEnd ClassNote: Note: The base class ConfigurationProvider mandates that you override the abstract methodInitialize. The Initialize method allows us to get access to the ConfigurationView (ie. a view of theconfiguration parameters as specified in the configuration files). The ILogFormatter interfacedefines the Format method. You use a XmlTextWriter over a StringWriter to return a stringcontaining XML.• Select Build | Build Solution to compile the complete solution.Task 2 – Use a custom log formatter• Make sure that any previous instance of the <strong>Enterprise</strong> Configuration Console is shut.• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.• Select File | Open Application and browse to the App.configfile located here [C:\Microsoft Hands-On-Lab\HOL-pnp04\Source\vb\exercises\ex03\begin\EnoughPI].• Right click on the Formatters node of the Logging and Instrumentation Application Block DistributorSettings. Select the New | Custom Formatter menu command.Page 195


Figure 3.1Create a new Custom Formatter• Select the Name field and rename the Custom Formatter as Xml Formatter.• Select the TypeName field and click the ellipses to display the Type Selector dialog.Figure 3.2Xml Formatter• Click the Load an Assembly button and browse to the EnoughPI.Logging.dll assembly here[C:\Microsoft Hands-On-Lab\HOL-pnp04\Source\vb\exercises\ex03\begin\EnoughPI\bin] and clickthe Open button.• Select the XmlFormatter class from the EnoughPI.Logging assembly and click the Ok button.Page 196


Figure 3.3Xml Formatter• Select the Console Destination for the General category of the Logging and InstrumentationApplication Block Distributor Settings. The Formatter is set to Text Formatter. Set the Formatter toXml Formatter.Similarly set the Formatter for the Console Destination for the Trace category to Xml Formatter.Figure 3.4Formatter• Select the File | Save All menu command to save the configuration.• In Visual Studio, select the Debug | Start menu command to run the application. Click the Calculatebutton. The log entries will be displayed in the applications Console Window.Page 197


Figure 3.5Never enough PITask 3 – More information• For more information on how and when to instrument applications, including using custom requesttracing to identify issues across multi-tiered applications, see "Using Advanced Microsoft® .NETPerformance and Operational Techniques" course which is part of the Mastering Industrial Strength.NET series. This course shows how to build custom providers for the enterprise logging block andalso extends <strong>Enterprise</strong> <strong>Library</strong> to support request tracing across Web services, with a mechanismthat would inter-operate with non-Microsoft technologies. The course also uses some freelyavailable profiling tools to help solve performance issues once a bottleneck has been identified.• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp04\Source\vb\exercises\ex03\end\EnoughPI.sln]Lab SummaryIn this lab you performed the following exercises.• Add Logging to an Application• Create and use a custom log sink• Create and use a custom log formatterPage 198


Lab 5 VB.NET Exception Handling BlockThis lab demonstrates the use of the <strong>Enterprise</strong> <strong>Library</strong> Exception Handling Block.Lab ObjectiveTime to complete: 30 minutesThe objectives of this lab are: add Exception Logging to an application; use a Replace Handler to hidesensitive information.• Logging Exceptions• Exception Handling StrategiesExercise 1: Logging ExceptionsIn this exercise you will take an application without exception handling, and add local and globalexception handlers that log the exceptions to the Event Log using the Exception ManagementApplication Block.Task 1 – Review the Application• Open the Puzzler [C:\Microsoft Hands-On-Lab\HOLpnp05\Source\vb\exercises\ex01\begin\Puzzler.sln]file, and build the solution.• Open the Puzzler.vb file in the designer. This application performs two functions, it checks thespelling of words against a dictionary (unix dict for size) and it uses the dictionary to generate a listof words that can be constructed from a character list.• There is currently no exception handling in the application. Run the application, and attempt to adda word which contains numbers to the dictionary (put 'ab123' in the word to check textbox and clickAdd Word.An unhandled exception will occur, which will break into the debugger. Click Continue to allow theapplication to exit and return to Visual Studio.Task 2 – Install <strong>Enterprise</strong> <strong>Library</strong> Instrumentation• If you have not already, install the enterprise instrumentation by selecting from the Start Menu: Start| All Programs | Microsoft patterns & practices | <strong>Enterprise</strong> <strong>Library</strong> | Install Services.Note: <strong>Enterprise</strong> <strong>Library</strong> has built-in instrumentation that increments performance counters andsends out WMI events. If these performance counters and event types are not registered, you willget warning events in your event log. If you do not wish to have the inbuilt instrumentation, removethe defines USEWMI;USEEVENTLOG;USEPERFORMANCECOUNTER from the Common project,and recompile it. See this post for more information.Page 199


Task 3 – Add Try/Catch Exception Handling• Select the PuzzlerUI project. Select the Project | Add Reference … menu command. Select theBrowse button and select the following assemblies located here [C:\Program Files\Microsoft<strong>Enterprise</strong> <strong>Library</strong>\bin].Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.ExceptionHandling.dll.Note: While this assembly is the only assembly required for the ExceptionHandling API, otherassemblies may need to be available in the bin directory to provide specific exception handlingfunctionality, which you will add later.• Right click Puzzler.vb in the solution explorer and select View Code. Find the btnAddWord_Clickmethod, and add a Try/Catch section around the AddWord and SetError calls. (Inserted code inbold):Private Sub btnAddWord_Click(ByVal sender As System.Object, ByVal e AsSystem.EventArgs) Handles btnAddWord.ClickTryPuzzlerService.Dictionary.AddWord(txtWordToCheck.Text)ErrorProvider1.SetError(txtWordToCheck, "")Catch ex As ExceptionIfMicrosoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.ExceptionHandling.ExceptionPolicy.HandleException(ex, "UI Policy") Then ThrowMessageBox.Show("Failed to add word " + txtWordToCheck.Text + ",please contact support.")End TryEnd SubNote: NOTE:It is very important to just use the Throw statement, rather than Throw Ex. If you have"Throw Ex", then the stack trace of the exception will be replaced with a stack trace starting at there-throw point, which is usually not the desired effect.Task 4 – Configure the application to use Exception Management• Add a new Application configuration file (App.config) to the PuzzlerUI project. Click on thePuzzlerUI project. Select the File | Add New Item menu command. Select ApplicationConfiguration File template. Leave the Name as App.config.Page 200


Figure 1.1Add the New Item• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.• Select File | Open Application and browse to the App.configfile located here [C:\Microsoft Hands-On-Lab\HOL-pnp05\Source\vb\exercises\ex01\begin\PuzzlerUI].• Right click on the Application and select New | Exception Handling Application Block.Page 201


Figure 1.2Add the Exception Handling Block• Add a new exception policy. Change the name of the new policy from Exception Policy to UI Policy.Page 202


Figure 1.3Remove the current nodeNote: The policy name here must match that specified in the code. Typically you would useconstants to help prevent typographical errors, especially since the exception handling typically isnot tested as thoroughly as normal code paths.• Add the exception type System.Exception to UI Policy.Page 203


Figure 1.4Create a new Exception TypeNote: This is a filter that specifies which exceptions should be processed by the exception handlingcode, and what handlers to run. In this case, all exceptions derived from System.Exception will beprocessed.• Set the PostHandlingAction for the System.Exception to None.Page 204


Figure 1.5Create a new Exception TypeNote: This will cause all exceptions to be handled internally within the exception handling code, andthe caller will not be requested to re-throw the exception with its catch block.• Add a Logging Handler for the System.Exception exception type.Page 205


Figure 1.6Create a new Logging HandlerNote: Note that this automatically includes the Logging and Instrumentation Application Block inyour configuration.• Set the LogCategory of the Logging Handler to General.• Save the current application in the <strong>Enterprise</strong> <strong>Library</strong> Configuration Console using File | Save All.Note: This will save the changes to your App.Config file as well as adding configuration files to yourproject directory for each block you have utilized.Task 5 – Change the application to include the exception logging assembly• Select the PuzzlerUI project. Select the Project | Add Reference menu command. Select theBrowse button and select the following assemblies located here [C:\Program Files\Microsoft<strong>Enterprise</strong> <strong>Library</strong>\bin].Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.ExceptionHandling.Logging.dll.Note: Because one of the goals of the <strong>Enterprise</strong> <strong>Library</strong> is to keep the blocks de-coupled, it ispossible to use the Exception Handling block without needing the Logging block (e.g. by creatingyour own exception handler). If you want to use the two blocks together, then you need to use thisassembly, which contains an Exception Handler that logs via the logging block.Page 206


Task 6 – Adding a Post-Build step to a VB.NET Project• The configuration settings file is found in the same directory as the application configuration file.Therefore you need to copy the exception handling block configuration file to the bin [C:\MicrosoftHands-On-Lab\HOL-pnp05\Source\vb\exercises\ex01\begin\PuzzlerUI\bin] directory before runningthe application. You could copy the file manually, but it would be preferable to automate the copyas part of the build process.VB.NET does not provide support for pre and post build events, but you achieve the same result byadding a C# class library.If you do not have C# installed, you can accomplish a similar effect by using the PrePostBuildRulesAdd-in you can get from Visual Studio .NET 2003 Automation Samples. See these tips for otheralternatives.Select the File | Add Project | New Project menu command.• In the New Project dialog select a C# Class <strong>Library</strong> Project, name it Post-Build and click OK.Note:Figure 1.7Add the New Project• Right click on the new Post-Build project and select Properties from the popup menu.• In the project properties dialog select Build Events under Common Properties and set the Post-buildevent command line property to the value below.copy "$(SolutionDir)\PuzzlerUI\*.config" "$(SolutionDir)\PuzzlerUI\bin"> NulPage 207


Figure 1.8Post-Build Property pagesNote: Redirecting the output to the Nul device will prevent the message 1 file(s) copied fromappearing in your build output.• Select the Build | Build Solution menu command. The last thing you will see in the build output isthe message:Performing Post-Build EventIf the post-build event fails for some reason, this will be shown as a build error.Note: Every time you build the application the post build event will run, copying all the config files inthe PuzzlerUI directory to the bin directory.Task 7 – Run the application• Run the Application. Try adding a number (type a number in the Word to check text box, and clickAdd Word) - you will see a MessageBox that says "Failed to add word …, please contact support".• Check the Event Log by using the Event Viewer (Control Panel | Administrative Tools | EventViewer). Look at the top of the Application Log. The exception should have been logged there.Page 208


Figure 1.9Check Event PropertiesNote: After the previous exception the Exception Management Block will have written an entry inthe Application Event Log using the Logging Block.• Close the application.Task 8 – Add Global Exception Handling• While it is possible to put Try/Catch sections around all the event handlers in an application, it isusually better to provide a single generic handler that fires whenever an unhandled exceptionoccurs within the application.• Open Puzzler.vb, and remove the exception handling from the btnAddWord_Click method:Private Sub btnAddWord_Click(ByVal sender As System.Object, ByVal e AsSystem.EventArgs) Handles btnAddWord.ClickPuzzlerService.Dictionary.AddWord(txtWordToCheck.Text)ErrorProvider1.SetError(txtWordToCheck, "")End Sub• Open the Startup.vb file.Application execution begins here. There are two events you can listen to for unhandledexceptions:The Application.ThreadException event is raised when an unhandled exception occurs on thethread that is executing the Application.Run method.If an exception is raised during that handler, or occurs on a different thread to the UI, then theAppDomain.UnhandledException event will fire.• Add the following method to Startup.vb, which will be used to handle exceptions.Public Shared Sub HandleException(ByVal ex As Exception, ByVal policy AsString)Dim rethrow As Boolean = FalsePage 209


Tryrethrow =Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.ExceptionHandling.ExceptionPolicy.HandleException(ex, policy)Catch innerEx As ExceptionDim errorMsg As String = "An unexpected exception occured whilecalling HandleException with policy '" + Policy + "'. "errorMsg += Environment.NewLine + innerEx.ToString()MessageBox.Show(errorMsg, "Application Error",MessageBoxButtons.OK, MessageBoxIcon.Stop)Throw exEnd TryIf rethrow ThenThrow ex ' WARNING: This will truncate the stack of theexceptionElseMessageBox.Show("An unhandled exception occurred and has beenlogged. Please contact support.")End IfEnd SubNote: This method will use the exception handling block, but will also display valid information ifthere is a problem with the exception handling block itself (e.g. missing configuration).It will alsodisplay a message to the user if the exception is swallowed (i.e. not re-thrown).• Add an event handler for the application ThreadException event.Public Shared Sub Application_ThreadException(ByVal sender As Object,ByVal e As System.Threading.ThreadExceptionEventArgs)HandleException(e.Exception, "UI Policy")End SubNote: This event handler will use the policy that you defined before, for the UI layer. In the nextexercise you will customize this policy to allow certain types of exception to escape and shut theapplication down.• Add an event handler for the app domain UnhandledException event.Public Shared Sub AppDomain_UnHandledException(ByVal sender As Object,ByVal e As System.UnhandledExceptionEventArgs)If TypeOf e.ExceptionObject Is System.Exception ThenHandleException(CType(e.ExceptionObject, System.Exception),"Unhandled Policy")End IfEnd SubNote: This handler will use a new policy, named Unhandled Policy, that you will set up in the nextexercise. The Unhandled Policy should almost always just log the exception, and not re-throw.• Connect the event handlers to the events at the begging of the application by including the followingcode in the Main method (Inserted code in bold).Public Shared Sub Main()AddHandler Application.ThreadException, AddressOfApplication_ThreadExceptionAddHandler AppDomain.CurrentDomain.UnhandledException, AddressOfAppDomain_UnHandledExceptionDim f As New PuzzlerApplication.Run(f)End Sub• Run the Application. Try adding a number (type a number in the Word to check text box and clickAdd Word) - you will see a MessageBox that displays An unhandled exception occurred and hasbeen logged. Please contact support. Look in the event log for the logged exception.Page 210


• Close the application.• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp05\Source\vb\exercises\ex01\end\Puzzler.sln]Exercise 2: Exception Handling StrategiesIn this exercise you will secure part of our application service with Code Access Security and then usea Replace Handler with the Exception Management Application Block to hide sensitive information fromclients. You will also show how you can filter which exceptions escape the top application layer.Task 1 – View the service modifications• Open the Puzzler2 [C:\Microsoft Hands-On-Lab\HOLpnp05\Source\vb\exercises\ex02\begin\Puzzler2.sln]file, and build the solution.• Open DictionaryService.vb in the PuzzlerService project.Note: You added a new class to the PuzzlerService project, named DictionaryService. This classacts as a Service Interface on top of the Dictionary class. Within this class you provide exceptionfiltering and transformation before sending the results back to the client. For techniques to interceptthe call and automatically transform and handle exceptions, see the "Developing Microsoft® .NETService-Oriented Applications" course of Mastering Industrial Strength .NET.Task 2 – Protect the Service's 'Add Word' functionality with Code Access Security• Open Dictionary.vbin the PuzzlerService project. Find the AddWord Function and decorate it with asecurity attribute as follows: _Public Shared Function AddWord(ByVal wordToAdd As String) As BooleanNote: This method can now only be executed by a user who is a member of the role GrandPoohBah, an unlikely situation.Note: Decorate the AddWord method in Dictionary.vbnotDictionaryService.vb.• Run the application, type a nonsense word (alphabetic - no numbers!) into the Word To Checktextbox (ensure that you have an error flashing), then click on the Add Word button. This will callthe service's AddWord function and throw a SecurityException, which you can check in the eventviewer.Page 211


Figure 2.1Event PropertiesNote: The SecurityException may be serialized from a Server to a Client (over Web Services) andcontains information that may help an attacker break our security. You would prefer to catch andlog the security exception on the server, then pass an exception containing less information to theclient.• Close the application.Task 3 – Configure the application to replace SecurityExceptions• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.• Select File | Open Application and browse to the App.config file located here [C:\Microsoft Hands-On-Lab\HOL-pnp05\Source\vb\exercises\ex02\begin\PuzzlerUI] (in the ex02/begin/PuzzlerUIproject).Note: Note: Do not use any existing Application Configuration that you may already have open -this will be for the previous exercise. Ensure that you Open the App.config for Ex02!• You have already created a policy, named Service Policy. By default, if a policy is empty, thenexceptions will just be re-thrown inside the catch block, so in effect the policy will do nothing.Add a new Exception Type: System.Security.SecurityException to the Service Policy. Change thePostHandlingAction for the SecurityException to ThrowNewException.Page 212


Figure 2.2Event Properties• Add a new Logging Handler to the SecurityException exception type, and change the followingvalues:LogCategory: GeneralTitle: Security Exception in Service LayerPage 213


Figure 2.3Create a new Logging Handler• Add a Replace Handler for the SecurityException exception type. Change the ExceptionMessageto Unauthorized Access, and select System.Security.SecurityException from mscorlib as thereplacement exception type from the type selector dialog.Page 214


Figure 2.4Create a new Replace HandlerNote: Although you have kept the type the same, by creating a new SecurityException, this will notprovide the client any of the stack information or internal security exception information, which couldcompromise security.Task 4 – Change the application to exit on a Security exception• Add a new exception type under the UI Policy policy of type System.Security.SecurityException.Leave the PostHandlingAction as NotifyRethrow, which will cause the handler for theApplication.ThreadException event to re-throw the exception, and shut down the application.Page 215


Figure 2.5Create a new Exception Type• Add a Logging Handler to the SecurityException under UI Policy, and change the property valuesto:Note: LogCategory: GeneralTitle: Security Exception in UI Layer• Save the current application in <strong>Enterprise</strong> <strong>Library</strong> Configuration using File | Save All.Task 5 – Test the Replace Handler• Run the application, type a nonsense (alphabetic) word into the Word To Check textbox (ensurethat you have an error flashing), then click on the Add Word button. If debugging click on Continuewhen it brings up the Unhandled exception message. Open the event log to look at the eventscreated. You can use the debugger to observe exactly what is happening and relate this to theException Handling Policies.Note: This time the Exception will be shown three times in the Event Log. First aSecurityException is thrown in Dictionary.cs. This is caught by the service layer(DictionaryService.cs) which applies Service Policy. This will cause the exception to be written tothe event log on the server (with all available information included) and then will throw a newreplacement SecurityException (without specific stack information). Second the replacingSecurityException is caught by the Application ThreadException handler in Startup.cs. This applies"UI Policy" which will write the exception to the event log on the client (the same machine in ourcase) and set the rethrow boolean to true which allows our code to decide to re-throw the secondPage 216


SecurityException. Third the re-thrown SecurityException is caught by our AppDomainUnhandledException handler (it was thrown from outside the Application.Run) which appliesUnhandled Policy. This will log the exception and pop up a MessageBox telling us that somethingwent wrong in the application. The AppDomain UnhandledException handler does not consumeexceptions, so the exception continues to bubble up to the runtime or debugger exception handler.This will cause a standard unhandled exception dialog box to be displayed.• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp05\Source\vb\exercises\ex02\end\Puzzler2.sln]Lab SummaryIn this lab you performed the following exercises.• Logging Exceptions• Exception Handling StrategiesPage 217


Lab 6 VB.NET Cryptography BlockThis lab demonstrates the use of the <strong>Enterprise</strong> <strong>Library</strong> Cryptography Block. Keeping secrets is bothimportant and difficult. Probably the most common secrets you wish to keep are connection strings thatoften include usernames and passwords, however the task of securing these is a handled for us by theConfiguration Application Block and was discussed in the Data Access topic earlier. This lab looks atsecuring non configuration data and using a hash to secure a password prior to transmission.Lab ObjectiveTime to complete: 30 minutesThe objectives of this lab are: keep non configuration data secret; authenticate in a secure manner.• Encrypt and Decrypt Secrets• Use a HashProvider to store a one-way hashed passwordExercise 1: Encrypt and Decrypt SecretsTask 1 – Review the Application• Open the Chatter [C:\Microsoft Hands-On-Lab\HOLpnp06\Source\vb\exercises\ex01\begin\Chatter.sln]file, and build the solution.• Open the Chat.vb file in the designer. When you run the application, two Chat forms will be opened,one called "Fred" and one called "Bill". Messages can be passed between these forms. The codebehind the Send and Decrypt buttons is very simple.• Run the application. Type some text in the upper textbox of one of the Chat windows and press theSend button, the message will appear in the second textbox of the other window, press the Decryptbutton on the second Chat window and the message will be appended to the third, multiline,textbox.Page 218


Figure 1.1DecryptNote: The third window with the "End Chat" button is the Application.Run window, when it closesthe other two windows will close.• Click the "End Chat" button to close the application.• The messages are being sent between the windows as plaintext, you will use the CryptographyBlock to encrypt and decrypt these communications using a symmetric key.Task 2 – Install <strong>Enterprise</strong> <strong>Library</strong> Instrumentation• If you have not already, install the enterprise instrumentation by selecting from the Start Menu: Start| All Programs | Microsoft patterns & practises | <strong>Enterprise</strong> <strong>Library</strong> | Install Services.Note: <strong>Enterprise</strong> <strong>Library</strong> has built in instrumentation that increments performance counters andsends out WMI events. If these performance counters and event types are not registered, then youwill get warning events in your event log. If you do not wish to have the inbuilt instrumentation,remove the defines USEWMI;USEEVENTLOG;USEPERFORMANCECOUNTER from theCommon project, and recompile it. See this post for more information.Task 3 – Change the project to use Encryption• Select the ChatUI project. Select the Project | Add Reference menu command. Select the Browsebutton and select the following assembly located here [C:\Program Files\Microsoft <strong>Enterprise</strong><strong>Library</strong>\bin]:Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Security.Cryptography.dll.• Add the following namespace inclusion to the list of namespaces at the top of the Chat.vb file (youmay have to right click and "View Code"):Imports Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Security.CryptographyPage 219


• Add the code below to the Chat class (added code in bold).Public Class ChatInherits System.Windows.Forms.Form... Other Code' String must match the provider name in thesecurityCryptographyConfiguration.config file.Private Const symmProvider As String = "ChatProvider"Public Partner As Chat... Other CodeEnd ClassNote: This constant will allow us to map to the appropriate security provider that you will define inour config files.• Modify the code in the btnSend_Click method in the Chat class, changing the it to use theCryptographer class (changed code in bold).Private Sub btnSend_Click(ByVal sender As Object, ByVal e AsSystem.EventArgs) Handles btnSend.ClicktxtConversation.AppendText(String.Format("{0} >> {1} " & vbCrLf,Me.Text, txtToSend.Text))Partner.txtToDecrypt.Text =Cryptographer.EncryptSymmetric(symmProvider, txtToSend.Text)End Sub• Modify the code in the btnDecrypt_Click method in the Chat class, changing the it to use theCryptographer class (changed code in bold).Private Sub btnDecrypt_Click(ByVal sender As Object, ByVal e AsSystem.EventArgs) Handles btnDecrypt.ClickDim plainText As String = Cryptographer.DecryptSymmetric(symmProvider,txtToDecrypt.Text)txtConversation.AppendText(String.Format("{0} >> {1} " & vbCrLf,Partner.Text, plainText))End SubTask 4 – Configure the application to use Symmetric key Cryptography• Add a new Application configuration file (App.config) to the ChatUI project. Click on the ChatUIproject. Select the File | Add New Item menu command. Select the Application configuration filetemplate. Leave the Name as App.config.Page 220


Figure 1.2Add New Item• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.• Select File | Open Application and browse to the App.configfile located here [C:\Microsoft Hands-On-Lab\HOL-pnp06\Source\vb\exercises\ex01\begin\ChatUI].• Right click on the Application and select New | Cryptography Application Block.Page 221


Figure 1.3Create a new Cryptography Application Block• Add a new symmetric provider. Select RijndaelManaged as the type, click the Generate button toautomatically generate a key, click OK.Page 222


Figure 1.4Create a new Symmetric Algorithm Provider• Change the Provider name to: "ChatProvider" so that it matches the name you put in our codeearlier. Your configuration should look like the image below.Page 223


Figure 1.5<strong>Enterprise</strong> <strong>Library</strong> Configuration• Save the current application in the <strong>Enterprise</strong> <strong>Library</strong> Configuration Console using File | Save All.Note: This will save the changes to your App.Config file as well as adding configuration files to yourproject directory for each block you have utilized.• Close the <strong>Enterprise</strong> <strong>Library</strong> Configuration application.Task 5 – Adding a Post-Build step to a VB.NET Project• The configuration settings file is expected to be found in the same directory as the applicationconfiguration file. Therefore you need to copy the cryptography block's configuration file to thebin directory before running the application. You could copy the file manually, but it would bepreferable to automate the copy as part of the build process.VB.NET does not provide support for pre and post build events, but you can achieve the sameresult by adding a C# class library.If you do not have C# installed, you can accomplish a similar effect by using the PrePostBuildRulesAdd-in you can get from Visual Studio .NET 2003 Automation Samples. See these tips for otheralternatives.Select the File | Add Project | New Project menu command.Page 224


• In the New Project dialog select a C# Class <strong>Library</strong> Project, name it Post-Build and click OK.Note:• Right click on the new Post-Build project and select Properties. from the popup menu.• In the project properties dialog select Build Events under Common Properties and set the Post-buildevent command line property to the value below.copy "$(SolutionDir)\ChatUI\*.config" "$(SolutionDir)\ChatUI\bin" > NulFigure 1.6Post-build Event Command LineNote: Redirecting the output to the Nul device will prevent the message 1 file(s) copied fromappearing in your build output.• Select the Build | Build Solution menu command. The last thing you will see in the build output isthe message:Performing Post-Build Event.If the post-build event fails for some reason, this will be shown as a build error.Note: Every time you build the application the post build event will run, copying all the config files inthe ChatUI directory to the bin directory.Task 6 – Run the Application• Run the application. Type some text in the upper textbox of one of the Chat windows and press theSend button, the message will appear encrypted in the second textbox of the other window, pressthe Decrypt button on the second Chat window and the plaintext of the message will be appendedto the third, multiline, textbox.Page 225


Figure 1.7DecryptNote: The strings being sent between the windows are now encrypted.• You may or may not have noticed that the application has become a little less stable now that youare using Cryptographer (hint, try sending an empty string). You would normally add some code tocheck the strings before you try to encrypt or decrypt them. For example you should check for zerolength strings in both the btnSend_Click and btnDecrypt_Click methods, and check inbtnDecrypt_Click that the strings you are about to decrypt are a multiple of 4 bytes long and onlycontain valid Base64 characters. These checks have been omitted for the sake of clarity.• Close the application.• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp06\Source\vb\exercises\ex01\end\Chatter.sln]Exercise 2: Use a HashProvider to store a one-way hashed passwordIn this exercise you will use a one-way hashing algorithm to encrypt a password stored in an XML file.Task 1 – Review the Application• Open the Password [C:\Microsoft Hands-On-Lab\HOLpnp06\Source\vb\exercises\ex02\begin\Password.sln]file, and build the solution.• Open the Login.vb file in the designer. This allows us to validate against a username and passwordthat are stored in plaintext in an XML file (user.xml). Open the ChangePassword.vb file. This allowsus to update the user's password.• Open User.vb. This is a class that holds the username and password and knows how to serializefrom and to an xml file (user.xml). Open user.xml - you can see the password in plain text, notwhat you would like.Page 226


Note: It is not necessarily a good idea to encrypt passwords using a symmetric key like the one youused in the previous exercise because if the key is compromised, so are all Your passwords.Onewayhashing algorithms are usually used with passwords. You would hash the password beforecomparing it with a hashed password stored somewhere (usually in a database). This way even ifthe database is compromised our passwords are still safe.Adding extra information 'Salt' to thepassword before encrypting it also makes the password much harder to crack. This is the defaultwith the Cryptography Block's hashing providers.• Run the application. This will open the login form, if you enter the correct password (P@ssw0rd)you will see a confirmation MessageBox (username is ignored, use 'Fred' if you like). Click on the"Change Password" button to bring up the Change Password dialog box and change the password,then check user.xml again - you should see the new password.Task 2 – Change the application to use the Hash provider• Select the PasswordUI project. Select the Project | Add Reference menu command. Select theBrowse button and select the following assembly located here [C:\Program Files\Microsoft<strong>Enterprise</strong> <strong>Library</strong>\bin]:Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Security.Cryptography.dll.• Add the following namespace inclusion to the list of namespaces at the top of the user.vb file:Imports Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Security.Cryptography• Add the code below to the User class in the user.vb file (added code in bold). As long as it is insidethe class it doesn't matter where the code is placed.Public Class User... Other Code' String must match the provider name in thesecurityCryptographyConfiguration.config file.private const hashProvider as String = "PasswordHasher"... Other CodeEnd ClassNote: This constant will allow us to map to the appropriate security provider as defined in our configfiles.• Add a ChangePassword method to the User class. This will allow us to change the password to anencrypted version.Public Function ChangePassword(ByVal oldPassword As String, ByValnewPassword As String) As Boolean_Password = Cryptographer.CreateHash(hashProvider, newPassword)Return TrueEnd FunctionNote: For the sake of simplicity, you aren't bothering to check the old password.• Add a ValidatePassword method to the User class. This will allow us to check an entered passwordagainst an encrypted one.Public Function ValidatePassword(ByVal plainText As String) AsBooleanReturn Cryptographer.CompareHash(hashProvider, plainText, _Password)End FunctionPage 227


• Click on Login.vb double click the Login button to go to the btnLogin_Click method and change it tocall our new ValidatePassword method rather than comparing the strings.Replace:If txtPassword.Text = u.Password ThenMessageBox.Show("Login Successful")with:if u.ValidatePassword(txtPassword.Text) ThenMessageBox.Show("Login Successful")• Click on ChangePassword.vb, double click the Change Password button to go to thebtnChangePassword_Click method and change it to call our new ChangePassword method ratherthan changing the password property.Replace:If txtOldPassword.Text = u.Password Then' Go ahead and change the passwordu.Password = txtNewPassword.Textu.SaveUser()Me.Close()Elsewith:If u.ChangePassword(txtOldPassword.Text, txtNewPassword.Text) Thenu.SaveUser()Me.Close()ElseTask 3 – Configure the application to use a Hash Provider• Add a new Application configuration file (App.config) to the PasswordUI project. Click on thePasswordUI project. Select the File | Add New Item menu command. Select the Applicationconfiguration file template. Leave the Name as App.config.Page 228


Figure 2.1Add New Item• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.• Select File | Open Application and browse to the App.configfile located here [C:\Microsoft Hands-On-Lab\HOL-pnp06\Source\vb\exercises\ex02\begin\PasswordUI].• Right click on the Application and select New | Cryptography Application Block.Page 229


Figure 2.2Create a new Cryptography Application Block• Add a new hash algorithm provider. Select SHA1Managed as the type.Page 230


Figure 2.3Create a new HashAlgorithm Provider• Change the provider name to: "PasswordHasher" to match the name you used in the code earlierand leave SaltEnabled set to True. Your configuration should look like the image below.Page 231


Figure 2.4<strong>Enterprise</strong> <strong>Library</strong> Configuration• Save the current application in the <strong>Enterprise</strong> <strong>Library</strong> Configuration Console using File | Save All.Note: This will save the changes to your App.Config file as well as adding configuration files to yourproject directory for each block you have utilized.• Close the <strong>Enterprise</strong> <strong>Library</strong> Configuration application.Task 4 – Adding a Post-Build step to a VB.NET Project• The configuration settings file is expected to be found in the same directory as the applicationconfiguration file. Therefore you need to copy the cryptography block's configuration file to thebin directory before running the application. You could copy the file manually, but it would bepreferable to automate the copy as part of the build process.VB.NET does not provide support for pre and post build events, but you can achieve the sameresult by adding a C# class library.If you do not have C# installed, you can accomplish a similar effect by using the PrePostBuildRulesAdd-in you can get from Visual Studio .NET 2003 Automation Samples. See these tips for otheralternatives.Select the File | Add Project | New Project menu command.Page 232


• In the New Project dialog select a C# Class <strong>Library</strong> Project, name it Post-Build and click OK.Note:• Right click on the new Post-Build project and select Properties. from the popup menu.• In the project properties dialog select Build Events under Common Properties and set the Post-buildevent command line property to the value below.copy "$(SolutionDir)\PasswordUI\*.config""$(SolutionDir)\PasswordUI\bin" > NulFigure 2.5Post-build Event Command LineNote: Redirecting the output to the Nul device will prevent the message 1 file(s) copied fromappearing in your build output.• Select the Build | Build Solution menu command. The last thing you will see in the build output isthe message:Performing Post-Build Event.If the post-build event fails for some reason, this will be shown as a build error.Note: Every time you build the application the post build event will run, copying all the config files inthe PasswordUI directory to the bin directory.Task 5 – Run the application• Run the application. Click on Change Password to bring up the Change Password dialog.Note: The stored password is unencrypted, you need to update this to an encrypted versionbefore you try to login.Page 233


• Enter P@ssw0rd for the old and new passwords, then click the Change Password button.Figure 2.6Change PasswordNote: This will call our new ChangePassword method and update the password in the xml file. If thepassword change is successful, the dialog will close.Note that normally you would have set apassword character for these fields so that the passwords would not be displayed. In this case theyhave been left clear so that you can see exactly what is going on.• Try to login with any username and the password you just entered. This should work. Otherpasswords should fail.• Close the application and open the user.xml file. You should see an encrypted version of ourpassword in the file - much safer.Task 6 – More information• For an example of using the cryptography block to encrypting security cookies for use with WS-Security Secure Conversation, see the "Using Advanced Microsoft® .NET Application SecurityTechniques" course which is part of the Mastering Industrial Strength .NET series.• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp06\Source\vb\exercises\ex02\end\Password.sln]Lab SummaryIn this lab you performed the following exercises.• Encrypt and Decrypt Secrets• Use a HashProvider to store a one-way hashed passwordPage 234


Lab 7 VB.NET <strong>Enterprise</strong> <strong>Library</strong> Security Hands On LabThis lab demonstrates the use of the <strong>Enterprise</strong> <strong>Library</strong> Security Application Block. Note: This lab requiresSQL Server to be installed.Lab ObjectiveTime to complete: 30 minutesThe objectives of this lab are: to use the Security Application Block to add authentication andauthorization to an application; to use the Security Database Administration Console; to use the<strong>Enterprise</strong> <strong>Library</strong> Configuration Tool to configure security providers; to use the Security Applicationblock to add personalization to an application.• Secure an Application• Use Task Based Authorization In an Application• Application PersonalizationExercise 1: Secure an ApplicationIn this exercise you will add authentication and role based authorization to an existing application. Youwill configure application users and roles via the Security Database Administration Console. You willconfigure security providers via the <strong>Enterprise</strong> <strong>Library</strong> Configuration Tool.Task 1 – Creating the Security Database and Populating Users• Open the BugSmak [C:\Microsoft Hands-On-Lab\HOLpnp07\Source\vb\exercises\ex01\begin\BugSmak.sln]file, and build the solution.• Security information (users, passwords, roles etc) will be stored in a SQL Server database. Ourapplication will use the <strong>Enterprise</strong> <strong>Library</strong> QuickStarts database (EntLibQuickStarts) which includesall the tables and stored procedures we require. Browse to the QuickStarts\Security [C:\ProgramFiles\Microsoft <strong>Enterprise</strong> <strong>Library</strong>\QuickStarts\Security\] directory (or click here [C:\ProgramFiles\Microsoft <strong>Enterprise</strong> <strong>Library</strong>\QuickStarts\Security\]) and double click on theSetUpQuickStartsDB.bat batch file.• Run the Security Database Console. From the Windows Start menu select All Programs | Microsoftpatterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | Security Database Console.• Select the QuickStarts Database Authentication Provider and click the Connect button.Figure 1.1Database Authentication ProviderPage 235


• Click the New User button. Enter a new user (user name is Student, password is P@ssw0rd [The 0is a the number zero) and click the Save button.Note: The new user is added to the Users table. The database saved password is a SHA1 saltedhash of the real password.• Click the


Figure 1.3Select Users in Role EmployeeNote: Student is now a member of the Developer, and Employee roles.• Add another user with the following attributes:Username: BossPassword: P@ssw0rdRoles: Manager, Employee• Close the Security Database Console.Task 2 – Adding Authentication to the Application• In Visual Studio select the BugSmak project. Select the Project | Add Reference menu command.Select the Browse button and then select the following assemblies located here [C:\ProgramFiles\Microsoft <strong>Enterprise</strong> <strong>Library</strong>\bin].Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Configuration.dll(All Blocks depend on the Configuration Application Block for handling configuration files etc. Wemust include this assembly for the configuration of the AuthenticationProvider).Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Security.dll(Contains the security application block API).• Open the SecurityHelper.vb file in the Helpers directory, and add the following namespace.Imports Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Security• Authenticate the user login by adding the following code to the Authenticate method of theSecurityHelper class.Public Shared Function Authenticate(ByVal username As String, ByValpassword As String) As BooleanDim authenticated As Boolean = FalsePage 237


' TODO: Authenticate CredentialsDim credentials As NamePasswordCredentialcredentials = New NamePasswordCredential(username, password)Dim authprovider As IAuthenticationProviderauthprovider = AuthenticationFactory.GetAuthenticationProvider()Dim identity As IIdentityauthenticated = authprovider.Authenticate(credentials, identity)If Not authenticated ThenThrow New SecurityException("Invalid username or password.")End If' TODO: Get RolesReturn authenticatedEnd FunctionNote: Use the AuthenticationFactory to retrieve the default authentication provider. Theauthentication provider will determine if the user credentials are correct, and if so, will create andreturn an identity object representing the user.• This is all the code required to authenticate a user. We now have to configure the security block touse the database security provider.Task 3 – Setting up a Database Authentication Provider with the <strong>Enterprise</strong> <strong>Library</strong>Configuration Tool• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.• Select File | Open Application and browse to the App.config file located here [C:\Microsoft Hands-On-Lab\HOL-pnp07\Source\vb\exercises\ex01\begin\BugSmak].• Right click on the Application node and select New | Security Application Block.Page 238


Figure 1.4Create a new Security Application BlockNote: A Security Application Block section is created to set the security configuration information. AConfiguration Application Block section is created to set the security configuration file information.• We will retrieve the authentication information from the database. Right click on the SecurityApplication Block | Authentication node of the and select the New | Database AuthenticationProvider menu command.Page 239


Figure 1.5Create a new Database Authentication ProviderNote: A Data Access Application Block section is created to configure the database connection. ACryptography Application Block section is created to configure the authentication password hashfunction.• Select the Data Access Application Block | Connection Strings | SQL Connection String node. Thisnode contains a collection of Name/Value pair properties.Set the value of the database property to EntLibQuickStarts.Set the value of the server property to localhost.The Integrated Security property should be left set to True.• Right click on the Cryptography Application Block | Hash Providers node and select the New | HashAlgorithm Provider menu command.Page 240


Figure 1.6Create a new HashAlgorithm Provider• Select the SHA1Managed type from the Type Selector dialog and click the Ok button.Figure 1.7Load an AssemblyNote: The Security Database Console we used earlier to create the Student user used a saltedSHA1 hash on the password.• Confirm the SaltEnabled property is set to True.Page 241


Figure 1.8Confirm SaltEnabled• Select the Security Application Block | Authentication | Database Provider node. Set the Databaseproperty to Database Instance, and the HashProvider property to SHA1Managed.Figure 1.9Create a new HashAlgorithm Provider• Select the Security Application Block node. Set the DefaultAuthenticationInstance to DatabaseProvider.Page 242


Figure 1.10Create a new HashAlgorithm Provider• Select the File | Save All menu command to save the configuration.• Select the BugSmak project in Visual Studio. Select the Project | Show All Files menu command.Note: three configuration files (securitycryptographyconfiguration.config,securityconfiguration.config and dataconfiguration.config) have been created.Task 4 – Adding a Post-Build Step to a VB.NET Project• The configuration settings files are expected to be found in the same directory as the applicationconfiguration file. Therefore we need to copy the configurations file to the bin directory beforerunning the application. We could copy the file manually, but it would be preferable to automate thecopy as part of the build process.Visual Basic .NET does not provide support for pre and post build events, but we can achieve thesame result by adding a C# class library.If you do not have C# installed, you can accomplish a similar result by using the PrePostBuildRulesAdd-in from Visual Studio .NET 2003 Automation Samples. See these tips for other alternatives.Select the File | Add Project | New Project menu command.• In the New Project dialog select a C# Class <strong>Library</strong> Project, name it Post-Build and click OK.Note:• Right click on the new Post-Build project and select Properties from the pop-up menu.Page 243


• In the project properties dialog select Build Events under Common Properties and set the Post-buildevent command line property to the value below.copy "$(SolutionDir)\BugSmak\*.config" "$(SolutionDir)\BugSmak\bin" >NulFigure 1.11Build EventsNote: Redirecting the output to the Nul device will prevent the message 1 file(s) copied fromappearing in your build output.• Select the Build | Build Solution menu command. The last thing you will see in the build output isthe message:Performing Post-Build Event.If the post-build event fails for some reason, this will be shown as a build error.Note: Every time you build the application the post build event will run, copying all the config files inthe BugSmak directory to the bin directory.Task 5 – Adding the Database Provider and Running the Application• In Visual Studio select the BugSmak project. Select the Project | Add Reference menu command.Select the Browse button and then select the following assemblies located here [C:\ProgramFiles\Microsoft <strong>Enterprise</strong> <strong>Library</strong>\bin].Microsoft.Practices.<strong>Enterprise</strong><strong>Library</strong>.Security.Database.Authentication.dll(Contains the DbAuthenticationProvider class).Page 244


Note: One of the design goals of the <strong>Enterprise</strong> <strong>Library</strong> is to allow usage of each application blockindependently of the others, but at the same time allow the blocks to be used together. This isaccomplished through using providers which use the other blocks. For example, the SecurityDatabase Authentication Provider uses the Data Access and Cryptography blocks. The providersspecified in the configuration file need to be accessible at runtime, which means installing them inthe Global Assembly Cache, or copying the files into the bin directory (in this case accomplished byadding a reference).• Select the Debug | Start menu command to run the application. The sign-in username andpassword (see LoginForm.vb) are preset for the Student user (password is P@ssw0rd). Sign in asStudent to confirm you correctly setup the security database and application configuration.Select the File | Sign Out menu command and attempt to sign in with an incorrect user name orpassword.Note: Notes: The application is the skeleton of a simple bug tracking system. The application has askeleton for the following functions: Raise a Bug, Assign a Bug to a developer for resolution, andResolve a Bug.The password is case sensitive, but the username is not.• As you exit the application or sign out you may see an unhandled exception dialog box as shownbelow. Please ignore this (click continue).Figure 1.12Microsoft Development EnvironmentNote: This is due to exceptions thrown by existing declarative role-based security(PrincipalPermission Attributes) which will no longer be effective when we implement SecurityApplication Block task authorization later in the lab.• Close the application.Task 6 – Adding Role-Based Authorization• Select the Debug | Start menu command to run the application, and sign in as Student. Select theTasks | Raise Bug menu command. You are presented with the message "Sorry, you aren'tallowed to access that form". The task forms have PrincipalPermissions defined to limit useraccess to the forms.Currently we have implemented authentication, but have not determined the user roles.TaskRole RequiredPage 245


Raise BugEmployee, Developer, or ManagerAssign BugManagerResolve BugDeveloper.Note: The RaiseBug form (see RaiseBug.vb file in the TaskForms directory) demands that the userhave either the Developer, Employee, or Manager role. Attempting to run the form without thenecessary authorization results in a SecurityException which is caught by the MainForm (seeMainForm.vb).• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.• Select File | Open Application and browse to the App.config file located here [C:\Microsoft Hands-On-Lab\HOL-pnp07\Source\vb\exercises\ex01\begin\BugSmak].• Select the Security Application Block node. Right click on the Roles node and select the New | RoleDatabase Provider menu command.• Set the Database property to Database Instance.Figure 1.13Create a new Role Database ProviderPage 246


Figure 1.14Database Instance• Select the Security Application Block node. Set the DefaultRolesInstance to RolesDatabaseProvider.Figure 1.15Create a new Role Database Provider• Select the File | Save All menu command to save the configuration.• In Visual Studio, open the SecurityHelper.vb file in the Helpers directory, and add the following codeto Authenticate method.Public Shared Function Authenticate(ByVal username As String, ByValpassword As String) As BooleanDim authenticated As Boolean = False' TODO: Authenticate CredentialsDim credentials As NamePasswordCredentialcredentials = New NamePasswordCredential(username, password)Dim authprovider As IAuthenticationProviderauthprovider = AuthenticationFactory.GetAuthenticationProvider()Page 247


Dim identity As IIdentityauthenticated = authprovider.Authenticate(credentials, identity)If Not authenticated ThenThrow New SecurityException("Invalid username or password.")End If' TODO: Get RolesDim rolesprovider As IRolesProviderrolesprovider = RolesFactory.GetRolesProvider()Dim principal As IPrincipalprincipal = rolesprovider.GetRoles(identity)' Place user's principal on the threadThread.CurrentPrincipal = principalReturn authenticatedEnd FunctionNote: Use the RolesFactory to retrieve the default roles provider. The roles provider will retrievethe roles for the given identity and return a principal which contains the users identity and roles. Touse the authenticated principal in our application we place it on the Thread.• Select the Debug | Start menu command to run the application, and sign in as Student. Select theTasks | Raise Bug menu command.Select the other tasks off the Tasks menu.Note: The Student user is a member of the Developer, and Employee roles; and therefore isauthorized to perform the Raise Bug, and Resolve Bug tasks. Student is not a member of theManager role, and is therefore not authorized to perform the Assign Bug task.Sign out, and re-signin as Boss (password P@ssw0rd). You will now be able to perform the Assign Bug task.• Close the Application, the Solution and the <strong>Enterprise</strong> <strong>Library</strong> Configuration app.Note: Be careful which App.Config file you are opening as you work through these exercises. Eachexercise has its own config files and the <strong>Enterprise</strong> <strong>Library</strong> Configuration application will default tothe directory you had open previously (i.e. the previous exercise!).• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp07\Source\vb\exercises\ex01\end\BugSmak.sln]Exercise 2: Use Task Based Authorization In an ApplicationTask 1 – Adding Authorization Rules with the <strong>Enterprise</strong> <strong>Library</strong> Configuration Tool• Open the BugSmak [C:\Microsoft Hands-On-Lab\HOLpnp07\Source\vb\exercises\ex02\begin\BugSmak.sln]file, and build the solution.Page 248


• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.• Select File | Open Application and browse to the App.config file located here [C:\Microsoft Hands-On-Lab\HOL-pnp07\Source\vb\exercises\ex02\begin\BugSmak].• Right click on the Security Application Block | Authorization node and select the New | AuthorizationRule Provider menu command.Figure 2.1Create a new Authorization Rule Provider• Change the name of RuleProvider to BugSmak Rules. Right click on the BugSmak Rules node andselect the New | Rule menu command. The Rule Expression Editor dialog is displayed.Page 249


Figure 2.2Create a new Rule• Add the following Expression, and click the OK button.R:Developer OR R:Employee OR R:ManagerFigure 2.3ExpressionNote: The user must be in either Developer, Employee, or Manager role.• Rename the Rule to Raise Bug.Page 250


• Add the following Rules and Expressions:Rule NameExpressionRaise Bug **R:Developer OR R:Employee OR R:ManagerAssign BugR:ManagerResolve BugR:Developer OR R:ManagerFigure 2.4Create a new Rule** Already entered in previous step.Page 251


Figure 2.5Resolve Bug• Select the Security Application Block node. Set the DefaultAuthorizationInstance to BugSmakRules.Page 252


Figure 2.6DefaultAuthorizationInstance• Select the File | Save All menu command to save the configuration.Task 2 – Adding Task Based Authorization• In Visual Studio, select the RaiseBug.vb file in the TaskForms directory. Select the View | Codemenu command. The RaiseBug form no longer uses the hard-coded PrincipalPermissions. Ratherthe more flexible task authorization is checked in the constructor. Each task form has similar codein the constructor.Public Sub New()MyBase.New()End SubIf (Not SecurityHelper.Authorized(AuthRule.Raise)) ThenThrow New SecurityExceptionEnd If'This call is required by the Windows Form Designer.InitializeComponent()'Add any initialization after the InitializeComponent() callNote: Notes:For compile-time checking, the rule names (e.g. "Raise Bug") are mapped to constantsin the Constants.vbfile.Page 253


• Open the SecurityHelper.vb file in the Helpers directory, and add the following code to Authorizedmethod.Public Shared Function Authorized(ByVal rule As String) As BooleanDim authorize As Boolean = False' TODO: Task AuthorizeDim ruleProvider As IAuthorizationProviderruleProvider = AuthorizationFactory.GetAuthorizationProvider()authorize = ruleProvider.Authorize(Thread.CurrentPrincipal, rule)Return authorizeEnd FunctionNote: Use the AuthorizationFactory to retrieve the default authorization provider. The authorizationprovider will test the current principal (roles and identity) against the rule.• Select the Debug | Start menu command to run the application, and sign in as Student. Select theTasks | Raise Bug menu command.Select the other tasks off the Tasks menu.Note: Student (a member of the Developer and Employee roles) is not able to access the AssignBug form as Student is not a member of the Manager role as is required by the Assign Bug rule.• Close the Application, the Solution and the <strong>Enterprise</strong> <strong>Library</strong> Configuration app.• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp07\Source\vb\exercises\ex02\end\BugSmak.sln]Exercise 3: Application PersonalizationIn this exercise you will use the ProfileProvider to implement application personalization.Task 1 – Adding a Profile Database Provider with the <strong>Enterprise</strong> <strong>Library</strong> ConfigurationTool• Open the BugSmak [C:\Microsoft Hands-On-Lab\HOLpnp07\Source\vb\exercises\ex03\begin\BugSmak.sln]file, and build the solution.• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.• Select File | Open Application and browse to the App.configfile located here [C:\Microsoft Hands-On-Lab\HOL-pnp07\Source\vb\exercises\ex03\begin\BugSmak].• Right click on the Security Application Block | Profile node and select the New | Profile DatabaseProvider menu command.Page 254


Figure 3.1Create a new Profile Database Provider• Select the Profile Database Provider and set the Database property to EntLibQuickStarts.Figure 3.2Create a new Profile Database ProviderPage 255


• Select the Security Application Block node. Set the DefaultProfileInstance to Profile DatabaseProvider.Figure 3.3Create a new Profile Database Provider• Select the File | Save All menu command to save the configuration.Task 2 – Adding personalization• In Visual Studio, select the Debug | Start menu command to run the application and sign in asStudent. Select the Tasks | Raise Bug menu command.Select the Options | Form Color menu command. Select a color in the Color dialog and click theOK button.You will use the profile provider to preserve color changes between application restarts.• Close the application.• Open the ProfileInfo.vb file in the Helper directory. The ProfileInfo class is a serializable classwhich stores the profile information you preserve.• To save the profile, add the following code to the SaveProfile method in the ProfileHelper.vb file.Public Shared Sub SaveProfile(ByVal taskForm As BaseForm)' TODO: Save Profile' Collect profile informationIf _profile Is Nothing ThenPage 256


_profile = New ProfileInfoEnd If_profile.FormColor = taskForm.FormColor_profile.TextColor = taskForm.TextColor' Save Profile InformationDim profileProvider As IProfileProviderprofileProvider = ProfileFactory.GetProfileProvider()profileProvider.SetProfile(Thread.CurrentPrincipal.Identity,_profile)End Sub• To load the profile, add the following code to the LoadProfile method in the ProfileHelper.vb file.Public Shared Sub LoadProfile(ByVal taskForm As BaseForm)' TODO: Load & Apply Profile' Lookup profile informationIf _profile Is Nothing ThenDim profileProvider As IProfileProviderprofileProvider = ProfileFactory.GetProfileProvider()_profile =CType(profileProvider.GetProfile(Thread.CurrentPrincipal.Identity),ProfileInfo)End IfEnd Sub' Apply profileIf _profile Is Nothing Then_profile = New ProfileInfoEnd IftaskForm.FormColor = _profile.FormColortaskForm.TextColor = _profile.TextColorNote: Note:The ProfileHelper keeps a static instance of the ProfileInfo class so that the profileinformation is only read once from the database.• Select the Debug | Start menu command to run the application and sign in as Student. Select theTasks | Raise Bug menu command.Select the Options | Form Color menu command. Select a color in the Color dialog and click theOK button.Exit and Restart the application and sign in as Student. Select the Tasks | Raise Bug menucommand and note that your color choices are preserved.Sign out, then re-sign in as Boss. Select the Tasks | Raise Bug menu command and note that thecolor choice has returned to the original value.Task 3 – More informationPage 257


• For more information on how to implement security in distributed applications, see the "UsingAdvanced Microsoft® .NET Application Security Techniques" course as part of the MasteringIndustrial Strength .NET series. This uses <strong>Enterprise</strong> <strong>Library</strong> to incorporate security in distributedsolutions, including building your own security providers and integrating with external authorizationsystems (e.g. authorization manager).• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp07\Source\vb\exercises\ex03\end\BugSmak.sln]Lab SummaryIn this lab you performed the following exercises.• Secure an Application• Use Task Based Authorization In an Application• Application PersonalizationPage 258


Lab 8 VB.NET Build your own BlockWARNING: This is a code heavy lab, please use the N,P and insert keys to navigate through thisexercise, otherwise you may get mouse cut & paste injuries…Your reward for finishing this lab (or skipping to the end to run the end solution.) will be to have a Clippit like characterthat knows when you are writing a letter.This lab requires the String Resource Tool for localization, which you have included here [C:\Microsoft Hands-On-Lab\HOL-pnp08\Source\vb\exercises\]. Please install this if you don't have another version. The newest version canalways been found on the Readify project distributor Website.In this lab, you will abstract some functionality into a new application block. The steps to do this are:[Exercise 1]Create an abstract client API and plugin API, using the provider modelCreate configuration data for the provider factoryand a custom providerCreate a provider factory derived from the Configuration Block ProviderFactory classCreate aconcrete provider by implementing the plugin API.Create design time configuration assembly for the provider factoryconfiguration data and custom provider.[Exercise 2]Create configuration data for the included providers.Create design time configuration classes for the included providers.[Exercise 3]Implement external providers in a separate assembly.Other steps that would typically occur, which you won't cover in this lab:Build unit tests for the providers in the block.Document all the classes in the block (and integrate with VS.NET).Lab ObjectiveTime to complete: 60 minutesThe objectives of this lab are: abstract some functionality into an external application; support run-timeconfigurable multiple providers.• Review Current Application• Create a Notification Block• Add Strongly Typed Configuration Data• Add External ProvidersExercise 1: Review Current ApplicationIn this exercise, you will investigate the piece of re-usable functionality that you want to turn into anapplication block.Page 259


Task 1 – Run the application• Open the Ex01 [C:\Microsoft Hands-On-Lab\HOL-pnp08\Source\vb\exercises\ex01\begin\Ex01.sln]file, and build the solution.• Run the application, and type the following (do not copy & paste):dear sir,thank you for your interest in patterns, you hope you enjoy <strong>Enterprise</strong><strong>Library</strong>.yours sincerely,p&p teamNote: See the file SmartWords.txt in the EditorApplication project for a list of the phrases detected.• As you can see, some phrases are automatically matched, and associated text is displayed in thestatus bar at the bottom.Task 2 – Review the Application code• Close the application.• Open the file EditorForm.vb, right-click and select View Code.• Find the method DisplayNotification. You will change the method by abstracting out the notificationto the user, and make it configurable at deployment.• Close Visual Studio.Exercise 2: Create a Notification BlockThis exercise will create the notification application block, and support adding external providers.Task 1 – Create the abstract API• Open the Ex02 [C:\Microsoft Hands-On-Lab\HOL-pnp08\Source\vb\exercises\ex02\begin\Ex02.sln]file, and build the solution.• You have created a new project, called Notification (FourthCoffee.Framework.Notification.dll), whichwill contain the API for our application block, as well as a single provider. You have createdskeleton files for all the files you need to implement.• Open the file INotificationProvider.vb in the Notification project. Add the following interface withinthe namespace. This will be our provider interface.'' '' Interface exposed by any provider that supports notification'' Public Interface INotificationProviderInherits IConfigurationProviderPage 260


'' '' Display a message to the user.'' This method must not block execution, so if the message display'' will take any length of time, it should occur asynchronously.'' '' The parent form to use when displaying themessage'' The message to display to the userSub DisplayMessage(ByVal parent As Form, ByVal message As String)End Interface 'INotificationProviderNote: Note that the provider interface derives from IConfigurationProvider. This ensures that anyimplementation of the provider can be initialized through configuration. It also means, however, thatall clients of this API must also add a reference to the Configuration assembly.Task 2 – Create the configuration data for the block• Within the Configuration folder, open the NotificationSettings.vb file. Add the following class withinthe namespace definition: _Public Class NotificationSettingsPublic Const ConfigurationNamespace As String ="http://www.fourthcoffee.com/framework/03-01-2005/notification"Public Const SectionName As String = "notificationConfiguration"End Class 'NotificationSettingsNote: The XmlRoot attribute is important to specify the namespace of the XML as it appears whenserialized, required to make the XML unambiguous. You will reference these two public constantsthroughout the rest of the code.• Add the following properties and backing fields to the NotificationSettings class:Private _notificationProviders As New NotificationProviderDataCollectionPrivate _defaultNotificationProviderName As String = Nothing _Public ReadOnly Property NotificationProviders() AsNotificationProviderDataCollectionGetReturn _notificationProvidersEnd GetEnd Property _Public Property DefaultNotificationProviderName() As StringGetReturn _defaultNotificationProviderNameEnd GetSet(ByVal Value As String)_defaultNotificationProviderName = ValueEnd SetPage 261


End PropertyNote: The NotificationProviders property provides the data for the configured notification providers.Note that the type of the property should derive from ProviderDataCollection (defined in theConfiguration assembly), to support some of the design time functionality (as you shall showlater).The DefaultNotificationProviderName property will be used to allow the client of the block tonot specify a provider name, and use a default specified in configuration.The Xml* attributes arebeing used to customize the saved XML.• Open the NotificationProviderData.vb file, and implement the NotificationProviderData class. Thiswill contain the base class that all the configuration for the providers will derive from. This class isused for two purposes:To contain any common configuration data that all providers use. In this case you do not have anycommon configuration settings for all our providers (apart from Name and Type, which are definedin the ProviderData base class).This is also the location where you can add static XmlIncludeattributes that will allow the XmlSerializer to de-serialize derived classes. Of course, you can onlyadd XmlInclude attributes for classes in this assembly. You will see in the next exercise how toinclude additional classes for externally defined providers. _Public MustInherit Class NotificationProviderDataInherits ProviderDataEnd Class 'NotificationProviderData• Open the CustomNotificationData.vb file, and implement the CustomNotificationData class. Thisclass is responsible for holding the type information and supporting additional weakly typedconfiguration data for providers which do not have design time support. _Public Class CustomNotificationDataInherits NotificationProviderDataPrivate _typeName As StringPrivate _attributes As New NameValueItemCollection _Public Overrides Property TypeName() As StringGetReturn _typeNameEnd GetSet(ByVal Value As String)_typeName = ValueEnd SetEnd Property _Public ReadOnly Property Attributes() As NameValueItemCollectionGetReturn Me._attributesEnd GetEnd PropertyEnd Class 'CustomNotificationDataNote: NOTE: It is very important that the XmlRoot attribute exists, and specifies a namespace. If anamespace is not specified, then the configuration data cannot be de-serialized by theXmlSerializer (as the de-serializer will be looking for a blank namespace, when the XMLnamespace will actually be set by the containing XML elements).Page 262


• Open NotificationProviderDataCollection.vb. Implement the provider collection class:Public Class NotificationProviderDataCollectionInherits ProviderDataCollectionDefault Public Property Item(ByVal index As Integer) AsNotificationProviderDataGetReturn CType(GetProvider(index), NotificationProviderData)End GetSet(ByVal Value As NotificationProviderData)SetProvider(index, Value)End SetEnd PropertyDefault Public Property Item(ByVal name As String) AsNotificationProviderDataGetReturn CType(GetProvider(name), NotificationProviderData)End GetSet(ByVal Value As NotificationProviderData)SetProvider(name, Value)End SetEnd PropertyPublic Sub Add(ByVal providerData As NotificationProviderData)AddProvider(providerData)End Sub 'AddEnd Class 'NotificationProviderDataCollectionNote: This class is used by the Settings class to maintain the list of provider data. It should derivefrom the ProviderDataCollection class (rather than, for example, CollectionBase), because theconfiguration design time support can provide additional functionality with ProviderDataCollectionbased classes.Task 3 – Create the configuration view• The configuration view class is responsible for querying the configuration subsystem for the currentconfiguration information. By using this class, the provider will automatically get the latestconfiguration information, even if the configuration has changed on disk while the application isrunning.• Open the NotificationConfigurationView.vb file. Implement the NotificationConfigurationView classwith the following code:Public Class NotificationConfigurationViewInherits ConfigurationViewPublic Sub New(ByVal context As ConfigurationContext)MyBase.New(context)End Sub 'NewPublic Overridable Function GetNotificationSettings() AsNotificationSettingsReturnCType(ConfigurationContext.GetConfiguration(NotificationSettings.SectionName), NotificationSettings)End Function 'GetNotificationSettingsPublic Overridable Function GetNotificationProviderData(ByValPage 263


providerName As String) As NotificationProviderDataArgumentValidation.CheckForNullReference(providerName,"sinkName")ArgumentValidation.CheckForEmptyString(providerName, "sinkName")Dim settings As NotificationSettings = GetNotificationSettings()Dim sinkData As NotificationProviderData =settings.NotificationProviders(providerName)If sinkData Is Nothing ThenThrow NewConfigurationException(SR.NoSuchProviderDefined(providerName))End IfReturn sinkDataEnd Function 'GetNotificationProviderDataEnd Class 'NotificationConfigurationViewNote: The ConfigurationContext class will load and cache the configuration section defined in theapplication configuration. It will also automatically re-load the configuration on demand if it ischanged in the source (e.g. on disk).Task 4 – Create the notification provider factory• OpenNotificationProviderFactory.vb, and add the following class:Public Class NotificationProviderFactoryInherits ProviderFactoryPublic Sub New()MyClass.New(ConfigurationManager.GetCurrentContext())End Sub 'NewPublic Sub New(ByVal configurationContext As ConfigurationContext)MyBase.New(SR.HandlerFactoryName, configurationContext,GetType(INotificationProvider))End Sub 'NewPrivate Function GetConfigurationView() AsNotificationConfigurationViewReturn NewNotificationConfigurationView(Me.ConfigurationContext)End Function 'GetConfigurationView' TODO: Implement protected overloads to provide type information' TODO: Implement strongly-typed creation methodsEnd Class 'NotificationProviderFactory• Add the following methods, which are required for the base class factory implementation to work:Protected Overrides Function CreateConfigurationView() AsConfigurationViewReturn GetConfigurationView()End Function 'CreateConfigurationViewProtected Overrides Function GetDefaultInstanceName() As StringReturnGetConfigurationView().GetNotificationSettings().DefaultNotificationProviderNameEnd Function 'GetDefaultInstanceNameProtected Overrides Function GetConfigurationType(ByValconfigurationName As String) As TypePage 264


Dim typeName As String =GetConfigurationView().GetNotificationProviderData(configurationName).TypeNameReturn MyBase.GetType(typeName)End Function 'GetConfigurationType• Add the following methods, which provide strongly typed creation of the providers:Public Overloads Function GetNotificationProvider() AsINotificationProviderReturn CType(MyBase.CreateDefaultInstance(), INotificationProvider)End Function 'GetNotificationProviderPublic Overloads Function GetNotificationProvider(ByVal providerName AsString) As INotificationProviderReturn CType(MyBase.CreateInstance(providerName),INotificationProvider)End Function 'GetNotificationProviderTask 5 – Create a static Factory class for one-line creation of providers• Open the NotificationFactory.vb file, and create the following NotificationFactory class, whichprovides static helper methods to create an instance of the notification provider (default providerand named provider):Public NotInheritable Class NotificationFactoryPrivate Sub New()End Sub 'NewPublic Overloads Shared Function GetNotificationProvider() AsINotificationProviderDim factory As New NotificationProviderFactoryReturn factory.GetNotificationProvider()End Function 'GetNotificationProviderPublic Overloads Shared Function GetNotificationProvider(ByValproviderName As String) As INotificationProviderDim factory As New NotificationProviderFactoryReturn factory.GetNotificationProvider(providerName)End Function 'GetNotificationProviderEnd Class 'NotificationFactoryTask 6 – Implement a weakly typed provider• Open StatusBarNotificationProvider.vb. Within the file, you have partially implemented the StatusBar Notification Provider. This provider will search for a status bar in the form which is asking fornotification (or its parent), and then display the message for a certain length of time in the statusbar.• Derive the class from ConfgurationProvider, and implement the INotificationProvider interface(inserted code in bold):Public Class StatusBarNotificationProviderInherits ConfigurationProviderImplements INotificationProviderPage 265


Note: The ConfigurationProvider base class implements the IConfigurationProvider interface, andmanages the Configuration Name of the provider. This class must then override the Initializemethod of the ConfigurationProvider to be able to obtain configuration data later.• Implement the Initialize method with the following code:Private config As NotificationConfigurationView = NothingPublic Overrides Sub Initialize(ByVal configurationView AsConfigurationView)ArgumentValidation.CheckExpectedType(configurationView,GetType(NotificationConfigurationView))config = CType(configurationView, NotificationConfigurationView)End Sub 'Initialize• Implement the DisplayMessage method (from INotificationProvider) with the following code:Public Sub DisplayMessage(ByVal parent As Form, ByVal message As String)Implements INotificationProvider.DisplayMessageDim displayTimeout As Integer = 2000Dim configData As CustomNotificationData = _CType(config.GetNotificationProviderData(ConfigurationName),CustomNotificationData)if (Not configData.Attributes.GetNameValueItem("DisplayTimeout") IsNothing) thendisplayTimeout =Integer.Parse(configData.Attributes.GetNameValueItem("DisplayTimeout").Value)End IfDim bar As StatusBar = SearchForStatusBar(parent)Dim manager As New StatusBarManager(bar)manager.Display(message, displayTimeout)End Sub 'DisplayMessageNote: This code obtains the current display timeout value using the Custom notificationconfiguration data, if set, finds the status bar, and then uses the StatusBarManager class to displaythe message.Task 7 – Add design time support• You have now implemented all the run-time provider infrastructure that you need. You now need tocreate the design-time support for the configuration console. Build the solution and fix any compileerrors at this point.• Open NotificationProviderNode.vb in the Notification.Configuration.Design project. Implement theNotification Provider Node class, which is the design time representation of theNotificationProviderData class: _Public MustInherit Class NotificationProviderNodeInherits ConfigurationNode _Public MustOverride ReadOnly Property NotificationProviderData() AsNotificationProviderDataPage 266


Protected Overrides Sub OnSited()MyBase.OnSited()Site.Name = NotificationProviderData.NameEnd Sub 'OnSitedProtected Overrides Sub OnRenamed(ByVal e AsConfigurationNodeChangedEventArgs)MyBase.OnRenamed(e)NotificationProviderData.Name = e.Node.NameEnd Sub 'OnRenamedEnd Class 'NotificationProviderNodeNote: This class is abstract, as each of the provider types (at this point there is only the CustomNotification Provider), will have their own concrete xyzNode class that is used to manipulate theirdata.The Image tag is used to provide the bitmap that will be displayed in the tree. In this case, abitmap named NotificationProviderNode.bmp should be in the project as an embeddedresource.The OnSited and OnRenamed methods are use to pull and push the Name informationfrom/to the underlying NotificationProviderData instance (which is provided by the concretexyzNode class).• Open the CustomNotificationProviderNode.vb file, and implement theCustomNotificationProviderNode class, which is used to manipulate the CustomNotificationDataconfiguration class.Public Class CustomNotificationProviderNodeInherits NotificationProviderNodePrivate customNotificationData As customNotificationDataPublic Overrides ReadOnly Property NotificationProviderData() AsNotificationProviderDataGetReturn customNotificationDataEnd GetEnd Property' Initializes node with default data.Public Sub New()Me.customNotificationData = New customNotificationDataMe.customNotificationData.Name =SR.DefaultCustomNotificationProviderNodeNameEnd Sub 'New' Initializes node with loaded data.Public Sub New(ByVal customNotificationData AscustomNotificationData)Me.customNotificationData = customNotificationDataEnd Sub 'New' TODO: Add properties that provide design-time access toconfiguration classEnd Class 'CustomNotificationProviderNodeNote: This class is the design-time access class for the custom notification provider's configurationdata.• Add the Attributes property to the CustomNotificationProviderNode class: _Public ReadOnly Property Attributes() As NameValueItemCollectionPage 267


GetReturn customNotificationData.AttributesEnd GetEnd PropertyNote: The SRDescription and SRCategory attributes are attribute classes that use resources tospecify the Description and Category at runtime of the particular property in the property grid. Thisis used to support localization.The SR class is code-generated from the SR.strings file, which isalso located in the same project. The Custom Tool that is used to generate the strongly-typedresource class can be downloaded from the link at the top of this lab.• Add the Type property to the same class: _Public Overridable Property TypeName() As StringGetReturn customNotificationData.TypeNameEnd GetSet(ByVal Value As String)customNotificationData.TypeName = ValueEnd SetEnd PropertyNote: This property uses the EditorAttribute (from System.ComponentModel) to specify the editor touse when displaying this property in the property grid. In this case, the editor is the <strong>Enterprise</strong><strong>Library</strong> type selection dialog, which uses the BaseType attribute to filter the types available.TheRequired attribute (from the Configuration block) is used by Configuration Block validation to ensurethat the property has been set before the user can save the configuration in the console.• Open the NotificationSettingsNode.vb, and implement the NotificationSettingsNode class, whichrepresents the root node of the Notification Block: _Public Class NotificationSettingsNodeInherits ConfigurationNodePrivate _notificationSettings As NotificationSettingsPublic Sub New()MyClass.New(New NotificationSettings)End Sub 'NewPublic Sub New(ByVal notificationSettings As NotificationSettings)Me._notificationSettings = notificationSettingsEnd Sub 'New'' '' The configured name.'' _Public Overrides Property Name() As StringGetReturn MyBase.NameEnd GetSet(ByVal Value As String)MyBase.Name = ValueEnd SetEnd Property'' Page 268


'' Retrieves configuration data based on the current state of thenode.'' '' Configuration data for this node. _Public Overridable ReadOnly Property NotificationSettings() AsNotificationSettingsGet_notificationSettings.NotificationProviders.Clear()Dim node As NotificationProviderNodeFor Each node In Me.Nodes_notificationSettings.NotificationProviders.Add(node.NotificationProviderData)Next nodeReturn _notificationSettingsEnd GetEnd Property' TODO: Add Dynamic Node Management' TODO: Add Default Node handlingEnd Class 'NotificationSettingsNode• During the life-cycle of a node in the configuration console, it has a few chances to change the hostenvironment. When this node is added to the console (e.g. when an application configuration file isloaded), you need to add all the nodes for each of the notification providers configured. Similarly,when given the chance to add menu items, you want to add a menu item under this node to createnew notification providers. Add the following code to the NotificationSettingsNode class:Protected Overrides Sub OnSited()MyBase.OnSited()Site.Name = SR.DefaultNotificationSettingsNodeNameCreateDynamicNodes(_notificationSettings.NotificationProviders)End Sub 'OnSitedProtected Overrides Sub OnAddMenuItems()MyBase.OnAddMenuItems()CreateDynamicMenuItems(GetType(NotificationProviderNode))End Sub 'OnAddMenuItemsNote: The CreateDynamicNodes call will iterate through the NotificationProviders collection, andcreate nodes of the appropriate type based on the type of the instance of the object in thatcollection. The mapping from configuration data type to node data type is performed within aConfigurationDesignManager (as you will do later).CreateDynamicMenuItems, similarly, will iteratethrough any registered node types that are derived from the given type, and create menu items forthem.• You need to add a property to this class to allow the user to select a default notification provider.Once selected, you need to ensure that the name of the provider stored within theNotificationSettings class always matches the name of the provider within its ownNotificationProviderData class. You also need to take care of the case where the user selects aprovider as the default, and then deletes it. Unfortunately, there is no succinct way of doingthis, and so the following code is quite complex. In future versions of <strong>Enterprise</strong> <strong>Library</strong>, there maybe a shortcut way of implementing it.Add the following code to the NotificationSettingsNode class:Page 269


Private defaultNotificationProviderNode As NotificationProviderNode =Nothing _Public Property DefaultNotificationInstance() AsNotificationProviderNodeGetReturn defaultNotificationProviderNodeEnd GetSet(ByVal Value As NotificationProviderNode)Dim service As ILinkNodeService =CType(GetService(GetType(ILinkNodeService)), ILinkNodeService)Debug.Assert(Not (service Is Nothing), "Could not get theILinkNodeService")defaultNotificationProviderNode =CType(service.CreateReference(defaultNotificationProviderNode, Value,New ConfigurationNodeChangedEventHandler(AddressOfOnNotificationDefaultProviderRemoved), NewConfigurationNodeChangedEventHandler(AddressOfOnNotificationDefaultProviderRenamed)), NotificationProviderNode)Me._notificationSettings.DefaultNotificationProviderName =IIf(Not (defaultNotificationProviderNode Is Nothing),Me.defaultNotificationProviderNode.Name, String.Empty)End SetEnd PropertyPrivate Sub OnNotificationDefaultProviderRenamed(ByVal sender As Object,ByVal args As ConfigurationNodeChangedEventArgs)Me._notificationSettings.DefaultNotificationProviderName = IIf(Not(defaultNotificationProviderNode Is Nothing),Me.defaultNotificationProviderNode.Name, String.Empty)End Sub 'OnNotificationDefaultProviderRenamedPrivate Sub OnNotificationDefaultProviderRemoved(ByVal sender As Object,ByVal args As ConfigurationNodeChangedEventArgs)Me.defaultNotificationProviderNode = NothingEnd Sub 'OnNotificationDefaultProviderRemovedPrivate Sub ResolveDefaultNotificationNode()If _notificationSettings.DefaultNotificationProviderName Is Nothing_OrElse_notificationSettings.DefaultNotificationProviderName.Length = 0 ThenReturnEnd IfDefaultNotificationInstance = CType(Hierarchy.FindNodeByName(Me,NotificationSettings.DefaultNotificationProviderName),NotificationProviderNode)End Sub 'ResolveDefaultNotificationNodePublic Overrides Sub ResolveNodeReferences()MyBase.ResolveNodeReferences()ResolveDefaultNotificationNode()End Sub 'ResolveNodeReferencesNote: The Editor attribute will create a drop-down, which is populated with node instances whichderive from NotificationProviderNode (specified in the ReferenceType attribute).• The last class you need to implement is called a Configuration Design Manager. It is responsible forloading the configuration into the designer, registering node types and XML includes, and savingPage 270


the configuration.Open NotificationConfigurationDesignManager.vb, and un-comment theNotificationConfigurationDesignManager class.Note: The CreateCommands method is responsible for adding the new notification section menuitem to the root node of the application in the console.The RegisterNodeTypes method isresponsible for registering settings and provider node types, which allow the dynamic menu itemcreation to work.• Finally, in order for the configuration console to use the Configuration Design Manager youcreated, you need to put the assembly in the same directory as the configuration console, and usean assembly attribute to provide the type of the design manager.You have already changed the build directory to be the <strong>Enterprise</strong> <strong>Library</strong> bin directory (you cancheck in the project properties window).Open the AssemblyInfo.vb file in the Notification.Configuration.Design project, and add the followingattribute at the bottom:Task 8 – Run the configuration console• You have now completed the framework for the new application block. You now have an applicationblock that will appear within the configuration console, supports run-time configuration re-loads, andthat you can extend with additional providers at run-time.• Build the solution, and ensure that it built successfully.• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.• Select File | Open Application and browse to the App.configfile located here [C:\Microsoft Hands-On-Lab\HOLpnp08\Source\vb\exercises\ex02\begin\EditorApplication][ex02\begin\EditorApplication].• Right-click over the Application node, and add a new Notification section.Page 271


• Add a new custom notification provider.Figure 2.1Create a new Notification ProviderPage 272


Figure 2.2Create a new Custom Notification Provider• Click on the TypeName property. Click the elipsis, and select the typeStatusBarNotificationProvider.Figure 2.3Type Selector• Click on the Attributes property, and add a new attribute:Page 273


Note: Name: DisplayTimeoutValue: 3000Figure 2.4Display Timeout• Select the Notification section, and set the default provider to Custom Notification Provider.Figure 2.5<strong>Enterprise</strong> <strong>Library</strong> Configuration• Select the File | Save All menu command to save the configuration.Page 274


Task 9 – Change the client application• You have already created post-build steps, and added the required references and usingstatements.Within the EditorApplication project, right-click over the EditorForm.vb file and select View Code.Change the DisplayNotification method to (modified code in bold):Private Sub DisplayNotification(ByVal description As String)TryDim provider As INotificationProvider =NotificationFactory.GetNotificationProvider()provider.DisplayMessage(Me, description)Catch ex As ExceptionMessageBox.Show(ex.ToString())End TryEnd Sub 'DisplayNotificationTask 10 – Run the application• Run the application, and type the following (do not copy & paste):dear sir,thank you for your interest in patterns, you hope you enjoy <strong>Enterprise</strong><strong>Library</strong>.yours sincerely,p&p teamNote: See the file SmartWords.txt in the EditorApplication project for a list of the phrases detected.• Close the Configuration console, if it is running. Re-open it, but this time open the applicationconfiguration file from the ex02\begin\EditorApplication\bin directory (here). Try changing theDisplayTimeout attribute value in the configuration console while the application is running. It maytake up to 15 seconds for a change to take effect.• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp08\Source\vb\exercises\ex02\end\Ex02.sln]Exercise 3: Add Strongly Typed Configuration DataIn this exercise, you will change the status bar provider to use strongly typed configuration data.Task 1 – Add run-time Configuration• Open the Ex03 [C:\Microsoft Hands-On-Lab\HOL-pnp08\Source\vb\exercises\ex03\begin\Ex03.sln]file, and build the solution.• Open the new file StatusBarNotificationData.vb, which is in the Configuration folder of theNotification project.Page 275


Implement the StatusBarNotificationData class with the following code: _Public Class StatusBarNotificationDataInherits NotificationProviderData _Public Overrides Property TypeName() As StringGetReturnGetType(StatusBarNotificationProvider).AssemblyQualifiedNameEnd GetSet(ByVal Value As String)End SetEnd PropertyPrivate _displayTimeout As Integer = 2000 _Public Property DisplayTimeout() As IntegerGetReturn _displayTimeoutEnd GetSet(ByVal Value As Integer)_displayTimeout = ValueEnd SetEnd PropertyEnd Class 'StatusBarNotificationDataNote: The XmlRoot attribute is required to ensure that the namespace of the class is set, for thesame reasons as the CustomNotificationData above.The XmlIgnore attribute is used becausethe type of the provider does not need to be serialized, as it will always be the same. (TheTypeName property is used at runtime by the notification provider factory to specify the concretetype of the provider).The DisplayTimeout property will contain the timeout in ms, with a default of2000 ms.• Open the file NotificationProviderData.vb, and add the following XmlInclude attribute before theclass definition (inserted code in bold): _Public MustInherit Class NotificationProviderDataInherits ProviderDataEnd Class 'NotificationProviderDataNote: This is used to allow the Xml Serializer infrastructure to de-serialize theStatusBarNotificationData class when de-serializing the NotificationProvideDataCollection instance.• Open the file StatusBarNotificationProvider.vb, and change the DisplayMessage method as below(modified code in bold):Public Sub DisplayMessage(ByVal parent As Form, ByVal message As String)Implements INotificationProvider.DisplayMessageDim configData As StatusBarNotificationData = _CType(config.GetNotificationProviderData(ConfigurationName),StatusBarNotificationData)Dim displayTimeout As Integer = configData.DisplayTimeoutDim bar As StatusBar = SearchForStatusBar(parent)Page 276


Dim manager As New StatusBarManager(bar)manager.Display(message, displayTimeout)End Sub 'DisplayMessageTask 2 – Add design time configuration support• Within the Notification.Configuration.Design project, open the new fileStatusBarNotificationNode.vb, and implement the StatusBarNotificationNode class:Public Class StatusBarNotificationNodeInherits NotificationProviderNodePrivate statusBarNotificationData As statusBarNotificationDataPublic Overrides ReadOnly Property NotificationProviderData() AsNotificationProviderDataGetReturn statusBarNotificationDataEnd GetEnd PropertyPublic Sub New()Me.statusBarNotificationData = New statusBarNotificationDataMe.statusBarNotificationData.Name =SR.DefaultSatusBarNotificationNodeNameEnd Sub 'NewPublic Sub New(ByVal statusBarNotificationData AsstatusBarNotificationData)Me.statusBarNotificationData = statusBarNotificationDataEnd Sub 'New _Public Property DisplayTimeout() As IntegerGetReturn statusBarNotificationData.DisplayTimeoutEnd GetSet(ByVal Value As Integer)statusBarNotificationData.DisplayTimeout = ValueEnd SetEnd PropertyEnd Class 'StatusBarNotificationNodeNote: In order to implement the design time support, the Provider Node class must:Supply theconfiguration data instance that contains the Name to its base classProvide constructors for a newinstance (adding a new provider), and for loading from a configuration file.Provide properties foreach of the data properties that should be exposed, along with optional design time attributes.• Open the file NotificationConfigurationDesignManager.vb, and add the following code at the end ofthe RegisterNodeTypes sub (inserted code in bold).Private Shared Sub RegisterNodeTypes(ByVal serviceProvider AsIServiceProvider)Dim nodeCreationService As INodeCreationService =ServiceHelper.GetNodeCreationService(serviceProvider)Dim nodeType As Type = GetType(CustomNotificationProviderNode)Dim entry As NodeCreationEntry =NodeCreationEntry.CreateNodeCreationEntryWithMultiples(NewAddChildNodeCommand(serviceProvider, nodeType), nodeType,GetType(CustomNotificationData),Page 277


SR.DefaultCustomNotificationProviderNodeName)nodeCreationService.AddNodeCreationEntry(entry)nodeType = GetType(StatusBarNotificationNode)entry = NodeCreationEntry.CreateNodeCreationEntryWithMultiples(NewAddChildNodeCommand(serviceProvider, nodeType), nodeType,GetType(StatusBarNotificationData),SR.DefaultSatusBarNotificationNodeName)nodeCreationService.AddNodeCreationEntry(entry)End Sub 'RegisterNodeTypesNote: This registers the new node (and associated data class) with the configuration console.Task 3 – Build and Configure• Build the application, and check that there are no errors.• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.• Select File | Open Application and browse to the App.config file located here [C:\Microsoft Hands-On-Lab\HOL-pnp08\Source\vb\exercises\ex03\begin\EditorApplication].• There is already a notification section configured. Add a new Status Bar Notification.• Set the DisplayTimeout to 3000.• Select the Notification section, and set the default provider to Status Bar Notification.Page 278


Figure 3.1Create a new Status Bar Notification• Select the File | Save All menu command to save the configuration.• Run the application, and test some the smart words.• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp08\Source\vb\exercises\ex03\end\Ex03.sln]Exercise 4: Add External ProvidersIn this exercise you will add two providers for notification, one based on MS Agent, and the other usinga scrolling title bar to display information.Task 1 – Review the extended providers• Open the Ex04 [C:\Microsoft Hands-On-Lab\HOL-pnp08\Source\vb\exercises\ex04\begin\Ex04.sln]file, and build the solution.• In the Notification.Extended project are two new providers, one to scroll the message in the formstitle bar, the other uses the Microsoft Agent technology to display the messages.• Open the FormTitleNotificationNode class in the Notification.Extended.Configuration.Design project.Note: This class demonstrates using the validation attributes that are part of the <strong>Enterprise</strong> <strong>Library</strong>configuration validation namespace (AssertRange), as well as showing mapping between datavalues in the runtime configuration, and design-time views.• Open the AgentNotificationNode class.Note: This class demonstrates using the FilteredFileNameEditor which allows browsing for afilename in the property grid, as well as the FileValidation attribute which will check that the fileexists.Task 2 – View the registration code for the external providers• The one difference when creating providers in external assemblies, compared to what you did withthe status bar notification provider, is managing the XML serialization.With providers defined in external assemblies, it is necessary to tell the configuration console toinclude those types used within the configuration for XML Serialization. This is done in theConfiguration Design Manager.Open the file ConfigurationDesignManager.vb within the Notification.Extended.Configuration.Designproject.The following code in RegisterXmlIncludeTypes method registers the types for Xml inclusion:private static void RegisterXmlIncludeTypes(IServiceProviderserviceProvider){IXmlIncludeTypeService xmlIncludeTypeService =serviceProvider.GetService(typeof(IXmlIncludeTypeService)) asPage 279


IXmlIncludeTypeService;xmlIncludeTypeService.AddXmlIncludeType(NotificationSettings.SectionName, typeof(FormTitleNotificationData));xmlIncludeTypeService.AddXmlIncludeType(NotificationSettings.SectionName, typeof(AgentNotificationData));}Task 3 – Configure the application• Run the <strong>Enterprise</strong> <strong>Library</strong> Configuration tool. From the Windows Start menu select All Programs |Microsoft patterns and practices | <strong>Enterprise</strong> <strong>Library</strong> | <strong>Enterprise</strong> <strong>Library</strong> Configuration.• Select File | Open Application and browse to the App.config file located here [C:\Microsoft Hands-On-Lab\HOLpnp08\Source\vb\exercises\ex04\begin\EditorApplication][ex04\begin\EditorApplication].• Create a new notification provider for each of the types, Agent Notification & Form Title Notification.• Set the default notification provider to be the Agent provider.• Close the Configuration Console.Task 4 – Add a reference to the extended notification assembly from the application• Add a project reference to the EditorApplication by Project | Add Reference, and selecting theNotification.Extended project.Task 5 – Run the application• Build and Run the application. Test that the Agent appears when typing in any of the magicphrases.• Run the enterprise console, and this time open the application configuration file from the runtimedirectory of the application (here [C:\Microsoft Hands-On-Lab\HOLpnp08\Source\vb\exercises\ex04\begin\EditorApplication\bin\])[ex04\begin\EditorApplication\bin\App.config].• Change the default provider, save, wait up to fifteen seconds, and then type some more magicwords (see SmartWords.txt in the EditorApplication project for the list). You will see the newprovider setting used. You can even change the agent that is being used on the fly, by selecting anew agent file.If you have Microsoft office installed, you can even use Clippit, which can be found in a directorylike \Microsoft Office\OFFICE11.Task 6 – For more informationPage 280


• For further information on the application of best practises, enterprise library and building enterprisescale applications, see the Mastering Industrial Strength .NET course, which also includes avalidation application block (in the "Developing Microsoft® .NET Service-Oriented Applications"course).• To check the finished solution click here. [C:\Microsoft Hands-On-Lab\HOLpnp08\Source\vb\exercises\ex04\end\Ex04.sln]Lab SummaryIn this lab you performed the following exercises.• Review Current Application• Create a Notification Block• Add Strongly Typed Configuration Data• Add External ProvidersPage 281

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!