Enterprise Library Hands on Labs

147
Enterprise Library for .NET Framework 2.0 Hands On Labs Enterprise Library Hands On Labs Lab 1: Data Access Block After completing this lab, you will be able to: Query a database using encrypted configuration settings. Use the data access block to read and update a database. Scenario This lab demonstrates the use of the Enterprise Library Data Access Application Block. It requires a (local)\SQLEXPRESS instance of SQL Server or SQL Server Express. Estimated Time To Complete this lab: 30 minutes. Exercise 1: Dynamic SQL with the Data Access Application Block This exercise demonstrates how to do basic database access, using the Data Access Block from the Enterprise Library. It will also show how to configure the block, providing runtime database selection, accessed via an alias in the code. First step 1. Open the SimpleData.sln file. Create the QuickStarts Database 1. As part of the installation of Enterprise Library, there is a batch file to set up the Quick Starts database. 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 accents distorted. To fix this problem, re-save the file as UTF16, and then re-run the batch file. 2. Open the file DataAccessQuickStarts.sql in Notepad, which can be found in the [Enterprise Install Dir]\QuickStarts\Data directory. Choose File | Save As. Select Unicode as the encoding. Should the Read only attribute warning appear, then select the file in Window Document made for self preparation (Abhishek Maitrey) 1

Transcript of Enterprise Library Hands on Labs

Page 1: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

Enterprise Library Hands On Labs

Lab 1: Data Access Block After completing this lab, you will be able to:

• Query a database using encrypted configuration settings. • Use the data access block to read and update a database.

Scenario

This lab demonstrates the use of the Enterprise Library Data Access Application Block. It requires a (local)\SQLEXPRESS instance of SQL Server or SQL Server Express.

Estimated Time To Complete this lab: 30 minutes.

Exercise 1: Dynamic SQL with the Data Access Application Block

This exercise demonstrates how to do basic database access, using the Data Access Block from the Enterprise Library. It will also show how to configure the block, providing runtime database selection, accessed via an alias in the code.

First step

1. Open the SimpleData.sln file.

Create the QuickStarts Database

1. As part of the installation of Enterprise Library, there is a batch file to set up the Quick Starts database. 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 accents distorted. To fix this problem, re-save the file as UTF16, and then re-run the batch file.

2. Open the file DataAccessQuickStarts.sql in Notepad, which can be found in the [Enterprise Install Dir]\QuickStarts\Data directory. Choose File | Save As. Select Unicode as the encoding. Should the Read only attribute warning appear, then select the file in Window

Document made for self preparation (Abhishek Maitrey) 1

Page 2: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

Explorer. Choose File | Properties and uncheck the Read only checkbox at the bottom of the window.

3. Click Save, and click Yes to overwrite the file. 4. Run the batch file SetUpQuickStartsDB.bat, from the same

directory.

Note: the database will be installed into the (local)\SQLEXPRESS instance.

Review the Application

1. Select the MainForm.cs file in the Solution Explorer. Select the View | Designer menu command.

This application contains a DataGrid and some menus. You will implement counting the available customers in the database, and then loading the customers into the datagrid, using the data access block.

Document made for self preparation (Abhishek Maitrey) 2

Page 3: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

Implement the Customer Menu Items

1. Select the CustomerManagment project. Select the Project | Add Reference … menu command. Select the Browse tab and select the following assemblies located here:

• Microsoft.Practices.EnterpriseLibrary.Common.dll, and • Microsoft.Practices.EnterpriseLibrary.Data.dll.

You will need to add the reference to the Common assembly, because the Data Block is instrumented and the IInstrumentationEventProvider is defined in the Common assembly.

2. Select the MainForm.cs file in the Solution Explorer. Select the View | Code menu command.

3. Add the following namespace inclusion to the list of namespaces at the top of the file:

using Microsoft.Practices.EnterpriseLibrary.Data;

4. Find the mnuCount_Click method (the Customer | Count menu click event handler), and add the following code (inserted code in bold):

private void mnuCount_Click(object sender, System.EventArgs e) { // TODO: Count Customers

Database db = null; db = DatabaseFactory.CreateDatabase("QuickStarts Instance");

int count = (int)db.ExecuteScalar( CommandType.Text, "SELECT COUNT(*) FROM Customers");

Document made for self preparation (Abhishek Maitrey) 3

Page 4: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

string message = string.Format( "There are {0} customers in the database", count.ToString());

MessageBox.Show(message); }

The code above first obtains an Enterprise Library database instance using the data access configuration for the database instance name "QuickStarts Instance" within the configuration file. The real database connection is not opened at this point.

The db.ExecuteScalar command has multiple overloads. The selected overload allows specifying some literal SQL to execute, and returns the result in a similar manner to the SqlCommand .ExecuteScalar method.

The db.ExecuteScalar method call is responsible for opening and closing the connection to the real database defined in the configuration file, as well as performing any instrumentation required.

5. Find the mnuLoad_Click method (the Customer | Load menu click event handler), and add the following code (inserted code in bold):

private void mnuLoad_Click(object sender, System.EventArgs e) { // TODO: Load Customers

Database db = null; db = DatabaseFactory.CreateDatabase();

DataSet ds = db.ExecuteDataSet( CommandType.Text, "SELECT * From Customers");

dataGrid1.DataSource = ds.Tables[0]; }

Document made for self preparation (Abhishek Maitrey) 4

Page 5: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

The db.ExecuteDataSet method is responsible for opening and closing the connection, as well as returning a new dataset filled with the results of the SQL Query, which may include multiple tables.

Note: This time we have not passed a database instance name to the CreateDatabase method. Rather the default database defined in the application configuration file will be used.

Configure the application

1. Add a new Application configuration file (App.config) to the CustomerManagement project. Click on the CustomerManagement project. Select the Project| Add New Item… menu command. Select Application configuration file template. Leave the Name as App.config.

2. We will use the Enterprise Library Configuration tool to configure our application. You may either start this tool from the Windows Start menu (select All Programs | Microsoft patterns and practices | Enterprise Library | Enterprise Library Configuration) and open the App.config file located here.

Document made for self preparation (Abhishek Maitrey) 5

Page 6: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

Alternatively you may configure Visual Studio to open the configuration file with the tool, as described below.

3. Select the App.config file in the Solution Explorer. Select the View | Open With… menu command. The OpenWith dialog is displayed. Click the Add button.

4. In the Add Program dialog, set the Program name to the EntLibConfig.exe file found here. Set the Friendly name to Enterprise Library Configuration. Click the OK button.

Document made for self preparation (Abhishek Maitrey) 6

Page 7: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

Visual Studio will pass the configuration file (App.config) to the EntLibConfig.exe program as a command line parameter.

5. In the Open With dialog, select the newly entered Enterprise Library Configuration program and click the OK button.

6. If you have a connectionStrings section defined in your machine.config file, you will find that the Enterprise Library Configuration tool automatically creates a Data Access Application Block for you.

If so select the Data Access Application Block | Connection Strings node and select the Action | New | Connection String menu command.

Document made for self preparation (Abhishek Maitrey) 7

Page 8: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

7. Alternatively, if you do not have a Data Access Application Block defined, right click on the Application and select New | Data Access Application Block.

Document made for self preparation (Abhishek Maitrey) 8

Page 9: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

8. Select the Data Access Application Block | Connection Strings | Connection String node. Change the Name property to QuickStarts Instance.

Document made for self preparation (Abhishek Maitrey) 9

Page 10: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

This name has to match the name you used in the code. This is how you can create an alias in code that maps to the concrete database instance you wish to use at runtime.

9. Select the Database node for this connection string. Change the Value property on the right hand side to EntLibQuickStarts.

Document made for self preparation (Abhishek Maitrey) 10

Page 11: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

10. Similarly, select the Server node, and set its Value to "(local)\SQLEXPRESS".

Document made for self preparation (Abhishek Maitrey) 11

Page 12: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

11. Select the Data Access Application Block node. Set the DefaultDatabase property is to the QuickStarts Instance.

Document made for self preparation (Abhishek Maitrey) 12

Page 13: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

12. Select the File | Save All menu command to save the application configuration. Close the Enterprise Library Configuration tool.

Run the application

1. Select the Debug | Start Without Debugging menu command to run the application. Click on Yes in the dialog box warning about the app.config being modified outside the source editor.

Select the Customers | Count menu command to view the number of customers in the database, and use the Customers | Load menu command to fill the DataGrid.

2. Close the application and Visual Studio .NET.

To check the finished solution, open the SimpleData.sln file.

Exercise 2: Stored Procedures and Updates with the Data Access Block

This exercise demonstrates using the data access block to wrap stored procedure access, as well as performing updates using a strongly typed DataSet.

First step

1. Open the DataEx2.sln file.

Add the Categories Table to the QuickStarts Database

1. Run the batch file SetUpEx02.bat, which can be found in the lab directory:

labs\cs\Data Access\exercises\ex02\DbSetup.

This adds a set of categories that you can use to filter the set of products you retrieve and manipulate.

Document made for self preparation (Abhishek Maitrey) 13

Page 14: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

Note: the modifications will be made to the (local)\SQLEXPRESS instance.

Review the application

1. Select the MainForm.cs in the Solution Explorer. Select the View | Designer menu command.

This application will allow us to select a particular category, load the products for that category, and then save any changes you make.

Implement database retrieval

1. Select the MainForm.cs in the Solution Explorer. Select the View | Code menu command. Add the following namespace inclusion to the list of namespaces at the top of the file:

The required references to the Data and Common assemblies have already been added.

using Microsoft.Practices.EnterpriseLibrary.Data;

2. 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 that you can hold onto this, as the Database instance does not represent an open connection, but instead represents a reference to a database.

If you had a SqlConnection object instead, then you would not be complying with the Acquire Late, Release Early model.

3. Find the MainForm_Load method, and insert the following code to obtain the set of categories using a DataReader:

Document made for self preparation (Abhishek Maitrey) 14

Page 15: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

private void MainForm_Load(object sender, System.EventArgs e) { this.cmbCategory.Items.Clear();

// TODO: Use a DataReader to retrieve Categories using (IDataReader dataReader = _db.ExecuteReader("GetCategories")) { // Processing code while (dataReader.Read()) { Category item = new Category( dataReader.GetInt32(0), dataReader.GetString(1), dataReader.GetString(2));

this.cmbCategory.Items.Add(item); } }

if (this.cmbCategory.Items.Count > 0) this.cmbCategory.SelectedIndex = 0; }

One of the overloads of the Database.ExecuteReader method takes a string, and an optional set of parameters. This overload will locate the stored procedure given by the string, read its meta-data (and cache it for future use), and set parameters with the values of any arguments provided.

Note: You are not doing any connection management here, but it is very important to Dispose 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.

4. Find the cmbCategory_SelectedIndexChanged method and insert the following code, which will read a subset of the products, based on the selected category drop down.

Document made for self preparation (Abhishek Maitrey) 15

Page 16: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

private void cmbCategory_SelectedIndexChanged(object sender, System.EventArgs e) { this.dsProducts.Clear();

Category selectedCategory = (Category)this.cmbCategory.SelectedItem; if (selectedCategory == null) return;

// TODO: Retrieve Products by Category _db.LoadDataSet( "GetProductsByCategory", this.dsProducts, new string[] { "Products" }, selectedCategory.CategoryId); }

There are two methods on the Database class that populate a DataSet; ExecuteDataSet and LoadDataSet. ExecuteDataSet returns a newly created DataSet, while LoadDataSet populates an existing one.

The overload used here takes a stored procedure as the first argument, the dataset to populate, a set of tables to map the result of the procedure into, and an arbitrary number of arguments. The additional arguments are mapped to any stored procedure arguments retrieved from the database metadata.

Implement updating the database

1. Find the btnSave_Click method. Insert the following code, which will update the database based on any changes in the dataset.

private void btnSave_Click(object sender, System.EventArgs e) { // TODO: Use the DataSet to update the Database System.Data.Common.DbCommand insertCommand = null; insertCommand = _db.GetStoredProcCommand("HOLAddProduct"); _db.AddInParameter(insertCommand, "ProductName",

Document made for self preparation (Abhishek Maitrey) 16

Page 17: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

DbType.String, "ProductName", DataRowVersion.Current); _db.AddInParameter(insertCommand, "CategoryID", DbType.Int32, "CategoryID", DataRowVersion.Current); _db.AddInParameter(insertCommand, "UnitPrice", DbType.Currency, "UnitPrice", DataRowVersion.Current);

System.Data.Common.DbCommand deleteCommand = null; deleteCommand = _db.GetStoredProcCommand("HOLDeleteProduct"); _db.AddInParameter(deleteCommand, "ProductID", DbType.Int32, "ProductID", DataRowVersion.Current); _db.AddInParameter(deleteCommand, "LastUpdate", DbType.DateTime, "LastUpdate", DataRowVersion.Original);

System.Data.Common.DbCommand updateCommand = null; updateCommand = _db.GetStoredProcCommand("HOLUpdateProduct"); _db.AddInParameter(updateCommand, "ProductID", DbType.Int32, "ProductID", DataRowVersion.Current); _db.AddInParameter(updateCommand, "ProductName", DbType.String, "ProductName", DataRowVersion.Current); _db.AddInParameter(updateCommand, "CategoryID", DbType.Int32, "CategoryID", DataRowVersion.Current); _db.AddInParameter(updateCommand, "UnitPrice", DbType.Currency, "UnitPrice", DataRowVersion.Current); _db.AddInParameter(updateCommand, "LastUpdate", DbType.DateTime, "LastUpdate", DataRowVersion.Current);

int rowsAffected = _db.UpdateDataSet( this.dsProducts, "Products", insertCommand, updateCommand, deleteCommand, UpdateBehavior.Standard); }

Document made for self preparation (Abhishek Maitrey) 17

Page 18: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

When updating a database, it is required to manually create the wrappers around the stored procedures, as it needs to know the mapping between the columns in the DataTable and the stored procedure arguments.

With this overload of the UpdateDataSet method, it is possible to get the Data Access Block to execute all the updates transactionally, by setting the UpdateBehaviour to Transactional. For more information, see the Enterprise Library documentation.

Run the application

1. Select the Debug | Start Without Debugging menu command to run the application.

Select a category from the drop down, and observe how it loads and saves the data.

More information

1. For more information on designing and implementing data access layers, and knowing whether to create strongly-typed data access layers (via code generation) versus weakly typed data access layer (e.g. the data access block) see the "Developing Microsoft .NET Applications Using Code Generation, UI Process and Abstraction"

®

course which is part of the Mastering Industrial Strength .NET series. This uses Enterprise Library as one of the options for abstracting data access, as well as implementing code generation to generate transaction aware data access layers.

To check the finished solution, open the DataEx2.sln file.

Exercise 3: Encrypting Connection Information

In this exercise, you will encrypt the configuration to prevent 'connection string discovery' by someone copying the application configuration file.

First step

1. Open the DataEx3.sln file.

Encrypt the Database Configuration

Document made for self preparation (Abhishek Maitrey) 18

Page 19: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

1. The .NET Framework version 2.0 natively supports protected configuration via the Configuration namespace.

You can use protected configuration to encrypt sensitive information, including user names and passwords, database connection strings, and encryption keys, in an application configuration file. Encrypting configuration information can improve the security of your application by making it difficult for an attacker to gain access to the sensitive information even if the attacker gains access to your configuration file.

Encryption and decryption is performed using a ProtectedConfigurationProvider class. The following list describes the protected configuration providers included in the .NET Framework:

• DPAPIProtectedConfigurationProvider. Uses the Windows Data Protection API (DPAPI) to encrypt and decrypt data.

• RsaProtectedConfigurationProvider. Uses the RSA encryption algorithm to encrypt and decrypt data.

Both providers offer strong encryption of data; however, if you are planning to use the same encrypted configuration file on multiple servers, such as a Web farm, only the RsaProtectedConfigurationProvider enables you to export the encryption keys used to encrypt the data and import them on another server.

By default, RSA key containers are tightly protected by NTFS access control lists (ACLs) on the server where they are installed. This improves the security of the encrypted information by restricting who can access the encryption key.

Note: Encrypted configuration information is decrypted when loaded into the memory that is used by your application. If the memory for

Document made for self preparation (Abhishek Maitrey) 19

Page 20: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

your application is compromised, the sensitive information from your protected configuration section might be compromised as well.

2. Select the ProductMaintenance project. Select the Project | Add Reference … menu command. Select the .NET tab and select the following assembly.

• System.Configuration.dll.

3. Select the Program.cs file in the Solution Explorer. Select the View | Code menu command. Add the following namespace inclusion to the list of namespaces at the top of the file:

using System.Configuration;

4. Add the following code to the ProtectConfiguration method.

static void ProtectConfiguration() { // TODO: Protect the Connection Strings string provider = "RsaProtectedConfigurationProvider";

Document made for self preparation (Abhishek Maitrey) 20

Page 21: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

Configuration config = null; config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);

ConfigurationSection section = config.ConnectionStrings;

if ((section.SectionInformation.IsProtected == false) && (section.ElementInformation.IsLocked == false)) { // Protect (encrypt) the "connectionStrings" section. section.SectionInformation.ProtectSection(provider);

// Save the encrypted section. section.SectionInformation.ForceSave = true; config.Save(ConfigurationSaveMode.Full); } }

Run the application

1. Select the Debug | Start Without Debugging menu command to run the application.

Note that it behaves the same as in the previous exercise.

2. Open the ProductMaintenance.exe.config file in the project bin\Debug directory located here. Note the connection strings are encrypted.

To check the finished solution, open the DataEx3.sln file.

Copyright © 2005 Microsoft. All Rights Reserved.

Document made for self preparation (Abhishek Maitrey) 21

Page 22: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

Enterprise Library Hands On Labs

Lab 2: Caching Block After completing this lab, you will be able to:

• Add persistent caching to a windows application. • Use background loading to populate a cache.

Scenario

This lab demonstrates the use of the Enterprise Library Caching Block. It requires a (local)\SQLEXPRESS instance of SQL Server or SQL Server Express.

Estimated Time To Complete this lab: 30 minutes.

Exercise 1: Using the Caching Block for performance

This exercise demonstrates how to implement caching using the Caching Block from the Enterprise Library. You will perform caching to speed up display of employee details, and then make the cache persistent, to support an offline scenario.

First step

1. Open the EmployeeBrowser.sln file.

Populate the QuickStarts database with employee data

1. Run the batch file SetCachingHOL.bat, which can be found in the lab directory:

labs\cs\Caching\setup.

This adds a set of employees which you will use to create an employee directory.

Document made for self preparation (Abhishek Maitrey) 22

Page 23: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

Note: the database will be installed into the (local)\SQLEXPRESS instance.

Review the application

1. This application is a browser for the employee contact details stored in the database. As part of the browser the application displays a photograph of the employee.

Although the application loads all employee records at startup, it doesn't load the photographs until they are requested. Images can be large, and retrieving all images (if they are to be viewed or not) could seriously slow the startup performance.

2. Select the MainForm.cs file in the EmployeeBrowser project. Select the View | Code menu command and locate the MainForm_Load method. The Form uses a class called EmployeeService to obtain the data to display. This in turn uses the EmployeeDataProvider class, as displayed in the picture below:

Currently, the EmployeeService just delegates directly to the EmployeeDataProvider class. You will enhance this class to use the Caching Block and to react appropriately when the application is offline.

3. Select the EmployeeDataProvider.cs file in the Solution Explorer. Select the View | Code menu command and locate the GetEmployeePhotoData method.

Note: The GetEmployeePhotoData method adds a 1 second delay to the call to simulate slow database access.

Document made for self preparation (Abhishek Maitrey) 23

Page 24: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

4. Select the Debug | Start Without Debugging menu command to run the application.

Browse through the employees.

Note: There is a significant delay while browsing through photos, even when you have viewed a photo previously.

Implement caching in the EmployeeService class

1. Select the EmployeeBrowser project. Select the Project | Add Reference… menu command. Select the Browse tab and select the following assembly located here:

• Microsoft.Practices.EnterpriseLibrary.Caching.dll. 2. Select the EmployeeService.cs file in the Solution Explorer. Select

the View | Code menu command. 3. Add the following namespace inclusion to the list of namespaces at the

top of the file:

using Microsoft.Practices.EnterpriseLibrary.Caching;

4. Add the following code to the GetEmployeePhoto method.

public static Bitmap GetEmployeePhoto(Guid employeeId) { byte[] photoData = null;

// TODO: Add Caching of Photo

// Attempt to retrieve from cache CacheManager cache = CacheFactory.GetCacheManager(); photoData = (byte[])cache[employeeId.ToString()];

Document made for self preparation (Abhishek Maitrey) 24

Page 25: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

// Retrieve from dataProvider if not in Cache if (photoData == null) { EmployeeDataProvider dataProvider = new EmployeeDataProvider(); photoData = dataProvider.GetEmployeePhotoData(employeeId);

cache.Add(employeeId.ToString(), photoData); }

// No data found. if (photoData == null) return null;

// Convert bytes to Bitmap using (MemoryStream ms = new MemoryStream(photoData)) { return new Bitmap(ms); } }

This method uses the factory model, as with the rest of Enterprise Library, to create a new CacheManager instance. This can be purely in-memory, or can be backed by a physical storage medium, 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 does not specify an expiration policy.

5. Add the following code to the ClearCache method, to allow the form to request the service to get new data.

public static void ClearCache() { // TODO: Clear Cache CacheManager cache = CacheFactory.GetCacheManager(); cache.Flush(); }

Document made for self preparation (Abhishek Maitrey) 25

Page 26: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

This method will remove all items from the cache.

"Open With…" Configuration Console

1. We will use the Enterprise Library Configuration tool to configure our application. You may either start this tool from the Windows Start menu (select All Programs | Microsoft patterns and practices | Enterprise Library | Enterprise Library Configuration) and open the App.config file located here.

Alternatively you may configure Visual Studio to open the configuration file with the tool, as described below.

2. Select the App.config file in the Solution Explorer. Select the View | Open With… menu command. The OpenWith dialog is displayed. Click the Add button.

3. In the Add Program dialog, set the Program name to the EntLibConfig.exe file found here. Set the Friendly name to Enterprise Library Configuration. Click the OK button.

Document made for self preparation (Abhishek Maitrey) 26

Page 27: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

Visual Studio will pass the configuration file (App.config) to the EntLibConfig.exe program as a command line parameter.

4. In the Open With dialog, select the newly entered Enterprise Library Configuration program and click the OK button.

Configure the application

Document made for self preparation (Abhishek Maitrey) 27

Page 28: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

1. Right click on the Application and select New | Caching Application Block.

2. Select the node Caching Application Block | Cache Managers | Cache Manager. Here some of the settings you can change to tune the performance of our cache. For now, leave the settings as they are.

Document made for self preparation (Abhishek Maitrey) 28

Page 29: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

3. Save the application configuration by choosing File | Save All. Close the Enterprise Library Configuration Console.

4. Select the App.config in the Visual Studio Solution Explorer. Select the View | Open menu command.

The App.config file now contains the caching configuration settings you added previously. Notice the backing store is Null Storage. That is, the cache will be stored in-memory.

Run the application

1. Select the Debug | Start Without Debugging menu command to run the application.

Document made for self preparation (Abhishek Maitrey) 29

Page 30: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

Browse through the employees and notice the performance difference for cached photos.

2. Close the application and close Visual Studio .NET.

To check the finished solution, open the EmployeeBrowser.sln file.

Exercise 2: Persistent Caching

This exercise demonstrates using persistent backing stores and expiration policies of an offline cache.

First step

1. Open the EmployeeBrowser.sln file.

Implement offline caching

1. Select the EmployeeServices.cs in the Visual Studio Solution Explorer. Select the View | Code menu command. Add the following using statement to the top of the file:

using Microsoft.Practices.EnterpriseLibrary.Caching.Expirations;

2. Locate the GetContactDetails method, and add the following code:

public static EmployeesDataSet GetContactDetails() { EmployeesDataSet dsEmployees = null;

// TODO: Add persistent caching with time-out

// Attempt to retrieve from cache CacheManager cache = CacheFactory.GetCacheManager(); dsEmployees = (EmployeesDataSet)cache[CACHE_KEY];

// Retrieve from dataProvider if not in Cache and Online if (dsEmployees == null && ConnectionManager.IsOnline)

Document made for self preparation (Abhishek Maitrey) 30

Page 31: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

{ EmployeeDataProvider dataProvider = new EmployeeDataProvider(); dsEmployees = dataProvider.GetEmployees();

// Expire in 2 days AbsoluteTime expiry = new AbsoluteTime(new TimeSpan(2, 0, 0, 0)); cache.Add(CACHE_KEY, dsEmployees, CacheItemPriority.High, null, new ICacheItemExpiration[] { expiry }); }

return dsEmployees; }

This code will only attempt to contact the database when the application is online.

This contact details are loaded into the cache using an overload of the Add method which allows the specifying of cache item priority, a cache item removal callback (which must be serializable for persistent caches), and a set of expiration policies. In this case, you do not want to allow our users to keep the employee data on their machines for more than 2 days without checking in. This helps to reduce the possibility of an employee taking the contact data to another company, as the caching infrastructure will remove the contents once they have expired.

3. Modify the GetEmployeePhoto method to not attempt to retrieve information from the database when offline (modified code in bold):

public static Bitmap GetEmployeePhoto(Guid employeeId) { byte[] photoData = null;

Document made for self preparation (Abhishek Maitrey) 31

Page 32: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

// Attempt to retrieve from cache CacheManager cache = CacheFactory.GetCacheManager(); photoData = (byte[])cache[employeeId.ToString()];

// TODO: Retrieve from dataProvider if not in Cache and Online if (photoData == null && ConnectionManager.IsOnline) { EmployeeDataProvider dataProvider = new EmployeeDataProvider(); photoData = dataProvider.GetEmployeePhotoData(employeeId);

cache.Add(employeeId.ToString(), photoData); }

// No data found. if (photoData == null) return null;

// Convert bytes to Bitmap using (MemoryStream ms = new MemoryStream(photoData)) { return new Bitmap(ms); } }

Configure the persistent cache

1. Select the App.config file in the Solution Explorer. Select the View | Open With… menu command. Select Enterprise Library Configuration and click the OK button.

2. Select the Caching Application Block | Cache Managers | Cache Manager node. Select the Action | New | Isolated Storage menu command.

Document made for self preparation (Abhishek Maitrey) 32

Page 33: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

3. Set the PartitionName property to EmployeeBrowser.

Document made for self preparation (Abhishek Maitrey) 33

Page 34: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

The Partition Name allows multiple caches to share the same Isolated storage location.

4. Save the configuration by selecting the menu File | Save All. Close the configuration console.

Run the application

1. Select the Debug | Start Without Debugging menu command to run the application.

Browse to a few of the employees, to load the cache with data, but don't browse all the employees (e.g. don't browse to Fuller, Dodsworth, or Callahan, so that their images are not cached). Close the application when you are finished browsing.

2. Select the ConnectionManager.cs file in Solution Explorer. Select the View | Code menu command. Modify the IsOnline property to simulate the application being started offline.

static public bool IsOnline { get { return false; } }

Normally this class would be responsible for testing server connectivity to determine whether the client is online and can access the database. See the Offline Application Block for ways to do this.

3. Select the Debug | Start Without Debugging menu command to restart the application. The application is now offline and does not access the database. Rather the employee contact details are retrieved from the cache, as are the images of the employees you browsed to while online. For the employees you had not browsed to while online (e.g. Fuller, Dodsworth, and Callahan), no image is displayed.

4. Close the application and close Visual Studio .NET.

To check the finished solution, open the EmployeeBrowser.sln file.

Exercise 3: Implement background caching

Document made for self preparation (Abhishek Maitrey) 34

Page 35: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

This exercise demonstrates the background loading of an offline cache.

First step

1. Open the EmployeeBrowser.sln file.

Implement background pre-loading of the cache when online

1. Select the EmployeeServices.cs in the Visual Studio Solution Explorer. Select the View | Code menu command. Add the following two methods, which will load the cache in the background.

private static void PopulateCache() { byte[] photoData = null;

EmployeesDataSet dsEmployees = GetContactDetails();

if (dsEmployees == null) return;

CacheManager cache = CacheFactory.GetCacheManager();

foreach (EmployeesDataSet.EmployeesRow employee in dsEmployees.Employees) { if (!cache.Contains(employee.EmployeeID.ToString())) { EmployeeDataProvider dataProvider = new EmployeeDataProvider(); photoData = dataProvider.GetEmployeePhotoData(employee.EmployeeID);

cache.Add(employee.EmployeeID.ToString(), photoData); }

Document made for self preparation (Abhishek Maitrey) 35

Page 36: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

} }

private delegate void PopulateCacheDelegate();

public static void BeginBackgroundLoad() { if (!ConnectionManager.IsOnline) return;

PopulateCacheDelegate mi = new PopulateCacheDelegate(PopulateCache); mi.BeginInvoke(null, null); }

The BeginBackgroundLoad method uses a delegate to begin the PopulateCache method on a background thread, handled by the .NET worker thread implementation. The PopulateCache method iterates through all the employees to retrieve and cache their image. Note that this is actually not a safe activity if the user can add or remove rows in the dataset on another thread (instead it would be better to Select the set of rows, and then iterate through them).

The Caching Block guarantees us thread safety when using the Cache, so it is safe to access from multiple threads at the same time.

2. Select the MainForm.cs file in Solution Explorer. Select the View | Code menu command. Locate the MainForm_Load method and the following code to start the background work.

private void MainForm_Load(object sender, EventArgs e) { this.ToolStripLabel1.Text = ConnectionManager.StatusText;

Document made for self preparation (Abhishek Maitrey) 36

Page 37: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

// Load data into the 'EmployeesDataSet'. EmployeesDataSet tempDataset = EmployeeService.GetContactDetails();

if (tempDataset != null) this.EmployeesDataSet.Merge(tempDataset);

// TODO: Start loading cache in the background EmployeeService.BeginBackgroundLoad(); }

Run the application

1. Select the Debug | Start Without Debugging menu command to run the application.

Don't bother browsing to any other employees, but rather wait for at least 10 seconds then exit the application.

While the application is online it will attempt to background cache the employee images. The cache is persisted to Isolated Storage, but uses a different PartitionName to the previous exercise.

2. Select the ConnectionManager.cs file in Solution Explorer. Select the View | Code menu command. Modify the IsOnline property to simulate the application being started offline.

static public bool IsOnline { get { return false; } }

3. Select the Debug | Start Without Debugging menu command to restart the application. The application is now offline and does not access the database, however all employee contact details and images should be cached.

Document made for self preparation (Abhishek Maitrey) 37

Page 38: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

More Information

1. For more information on how to use caching in building occasionally connected solutions, see the Mobile Devices course which is part of the Mastering Industrial Strength .NET series. This uses Enterprise Library to support caching of reference data and Web service requests, using both intrusive application code changes and proxy interception based offline handling techniques.

To check the finished solution, open the EmployeeBrowser.sln file.

Copyright © 2005 Microsoft. All Rights Reserved.

Document made for self preparation (Abhishek Maitrey) 38

Page 39: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

Enterprise Library Hands On Labs

Lab 3: Enterprise Library Logging Hands On Lab After completing this lab, you will be able to:

• Use the Enterprise Library Logging Application Block to implement logging in an application.

• Create and use custom TraceListeners. • Create and use custom LogFormatters.

Scenario

This lab demonstrates the use of the Enterprise Library Logging Application Block.

Estimated Time To Complete this lab: 30 minutes.

Exercise 1: Add Logging to an Application

In this exercise you will add logging and tracing to an existing application. You will configure TraceListeners via the Enterprise Library Configuration Tool.

First step

1. Open the EnoughPI.sln file.

About the application

1. Select the Debug | Start Without Debugging menu command to run the application.

The EnoughPI application calculates the digits of pi (π, the ratio of the circumference of a circle to its diameter). Enter your desired precision via the NumericUpDown control and click the Calculate button. Be prepared to wait if you want more than 500 digits of precision.

Document made for self preparation (Abhishek Maitrey) 39

Page 40: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

Adding Logging

1. Select the EnoughPI project. Select the Project | Add Reference … menu command. Select the Browse tab and select the following assembly located here.

• Microsoft.Practices.EnterpriseLibrary.Logging.dll. 2. Select the Calc\Calculator.cs file in Solution Explorer. Select the

View | Code menu command.

Add the following namespace inclusion at the top of the file:

using Microsoft.Practices.EnterpriseLibrary.Logging;

Document made for self preparation (Abhishek Maitrey) 40

Page 41: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

3. Log the calculation completion by adding the following code to the OnCalculated method in the Calculator.cs file.

protected void OnCalculated(CalculatedEventArgs args) { // TODO: Log final result LogEntry log = new LogEntry(); log.Message = string.Format("Calculated PI to {0} digits", args.Digits); log.Categories.Add(Category.General); log.Priority = Priority.Normal;

Logger.Write(log);

if (Calculated != null) Calculated(this, args); }

Create a new LogEntry, set parameters, then use the Logger to write the entry to one or more TraceListeners.

Notes:

• You have used constants for the Category and Priority rather than use hard-coded tags and integers (see Constants.cs in the EnoughPI.Logging project).

4. Log the calculation progress by adding the following code to the OnCalculating method in the Calculator.cs file.

protected void OnCalculating(CalculatingEventArgs args) { // TODO: Log progress Logger.Write( string.Format("Calculating next 9 digits from {0}", args.StartingAt), Category.General, Priority.Low );

Document made for self preparation (Abhishek Maitrey) 41

Page 42: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

if (Calculating != null) Calculating(this, args);

if (args.Cancel == true) { // TODO: Log cancellation Logger.Write("Calculation cancelled by user!", Category.General, Priority.High); } }

Notes:

• You have used an overload of the Write method on the Logger class to shortcut creating a LogEntry.

5. Log calculation exceptions by adding the following code to the OnCalculatorException method in the Calculator.cs file.

protected void OnCalculatorException(CalculatorExceptionEventArgs args) { // TODO: Log exception if (!(args.Exception is ConfigurationErrorsException)) { Logger.Write(args.Exception, Category.General, Priority.High); }

if (CalculatorException != null) CalculatorException(this, args); }

Notes:

• You must test that the exception type is not a ConfigurationErrorsException as you would not be able to use the Logger if it has not be correctly configured.

Document made for self preparation (Abhishek Maitrey) 42

Page 43: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

• You would normally use the Enterprise Library Exception Handling Application Block to create a consistent strategy for processing exceptions.

Enterprise Library Configuration

1. We will use the Enterprise Library Configuration tool to configure our application. You may either start this tool from the Windows Start menu (select All Programs | Microsoft patterns and practices | Enterprise Library | Enterprise Library Configuration) and open the App.config file located here.

Alternatively you may configure Visual Studio to open the configuration file with the tool, as described below.

2. Select the App.config file in the Solution Explorer. Select the View | Open With… menu command. The OpenWith dialog is displayed. Click the Add button.

3. In the Add Program dialog, set the Program name to the EntLibConfig.exe file found here. Set the Friendly name to Enterprise Library Configuration. Click the OK button.

Document made for self preparation (Abhishek Maitrey) 43

Page 44: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

Visual Studio will pass the configuration file (App.config) to the EntLibConfig.exe program as a command line parameter.

4. In the Open With dialog, select the newly entered Enterprise Library Configuration program and click the OK button.

Configure the application

Document made for self preparation (Abhishek Maitrey) 44

Page 45: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

1. Right click on the Application and select New | Logging Application Block.

2. The default Logging Application Block configuration defines a Category Source named General. Categories are simply text tags that you may apply to your log events to group them. The General category has one TraceListener reference (named Formatted EventLog TraceListener).

New categories may be added by right clicking on the Category Sources node and selecting New | Category. A category may have many TraceListener references (though none repeated), and a TraceListener may be referenced by many categories.

Document made for self preparation (Abhishek Maitrey) 45

Page 46: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

3. Select the Logging Application Block | Trace Listeners | Formatted EventLog TraceListener node.

Set the Source property to EnoughPI.

Document made for self preparation (Abhishek Maitrey) 46

Page 47: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

This TraceListener will log formatted entries (using the Text Formatter) to the windows event log.

4. Select the File | Save All menu command to save the application configuration. Close the Enterprise Library Configuration tool.

Run the application

1. Select the Debug | Start Without Debugging menu command to run the application. Enter your desired precision and click the Calculate button.

2. Run the Event Viewer. From the Windows Start menu select Administrative Tools l Event Viewer. View the Application log for messages from the EnoughPI source.

Document made for self preparation (Abhishek Maitrey) 47

Page 48: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

3. Double click on a log entry to view the formatted log message.

Document made for self preparation (Abhishek Maitrey) 48

Page 49: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

4. Exit the application.

Adding Tracing

1. Often you would like to time sections of our application. The Logging and Instrumentation Application Block includes tracing which allows us to book-end a section of code and log the execution time.

2. Select the Calc\Calculator.cs file in the Solution Explorer. Select the View | Code menu command.

public string Calculate(int digits) { StringBuilder pi = new StringBuilder("3", digits + 2); string result = null;

try { if (digits > 0)

Document made for self preparation (Abhishek Maitrey) 49

Page 50: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

{ // TODO: Add Tracing around the calculation using (new Tracer(Category.Trace)) { pi.Append("."); for (int i = 0; i < digits; i += 9) { CalculatingEventArgs args; args = new CalculatingEventArgs(pi.ToString(), i+1); OnCalculating(args);

// Break out if cancelled if (args.Cancel == true) break;

// Calculate next 9 digits int 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)); }

Document made for self preparation (Abhishek Maitrey) 50

Page 51: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

return result; }

Often you would like to time sections of our application. Tracing allows us to book-end a section of code and log the execution time.

The Tracer will stop timing, and log its end trace message, when it is disposed. The using block guarantees us that Dispose() will be called on the Tracer at the end of the block. Allowing the Garbage Collector to dispose of the Tracer will result in incorrect timings.

3. Select the App.config file in the Solution Explorer. Select the View | Open With… menu command. Select Enterprise Library Configuration and click the OK button.

4. Select the Logging Application Block node. Set the TracingEnabled property to True.

Document made for self preparation (Abhishek Maitrey) 51

Page 52: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

5. Add a new TraceListener. Select the Logging Application Block | Trace Listeners node. Select the Action | New | FlatFile TraceListener menu command.

6. Set the following properties:

• Formatter = Text Formatter.

Document made for self preparation (Abhishek Maitrey) 52

Page 53: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

7. Add a new Trace category. Select the Logging Application Block | Category Sources node. Select the Action | New | Category menu command.

Document made for self preparation (Abhishek Maitrey) 53

Page 54: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

8. Set the following properties:

• Name = Trace, and • SourceLevels = ActivityTracing.

Document made for self preparation (Abhishek Maitrey) 54

Page 55: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

The category name "Trace" was used in our code (see Constants.cs in the EnoughPI.Logging project). Using the ActivityTracing source level will restrict the trace logging to the start and end log entries only.

9. Right click on the new Trace category, and select the New | Trace Listener Reference menu command, and set the following property:

• ReferencedTraceListener = FlatFile TraceListener.

Document made for self preparation (Abhishek Maitrey) 55

Page 56: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

10. Select the File | Save All menu command to save the application configuration. Close the Enterprise Library Configuration tool.

11. Select the Debug | Start Without Debugging menu command to run the application. Enter your desired precision and click the Calculate button.

12. You may view the elapsed time for the trace in the trace.log file.

----------------------------------------

Timestamp: 13/12/2005 6:08:01 AM

Message: Start Trace: Activity '8c07ce3b-235b-4a51-bdcc-83a5997c989e' in method 'Calculate' at 71661842482 ticks

Category: Trace

Priority: 5

EventId: 1

Document made for self preparation (Abhishek Maitrey) 56

Page 57: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

Severity: Start

Title:TracerEnter

Machine: TAGGERT

Application Domain: EnoughPI.exe

Process Id: 6016

Process Name: C:\Program Files\Microsoft Enterprise Library\labs\cs\Logging\exercises\ex01\begin\EnoughPI\bin\Debug\EnoughPI.exe

Win32 Thread Id: 6092

Thread Name:

Extended Properties:

----------------------------------------

----------------------------------------

Timestamp: 13/12/2005 6:08:01 AM

Message: End Trace: Activity '8c07ce3b-235b-4a51-bdcc-83a5997c989e' in method 'Calculate' at 71662624219 ticks (elapsed time: 0.218 seconds)

Category: Trace

Priority: 5

EventId: 1

Severity: Stop

Title:TracerExit

Machine: TAGGERT

Application Domain: EnoughPI.exe

Process Id: 6016

Process Name: C:\Program Files\Microsoft Enterprise Library\labs\cs\Logging\exercises\ex01\begin\EnoughPI\bin\Debug\EnoughPI.exe

Win32 Thread Id: 6092

Document made for self preparation (Abhishek Maitrey) 57

Page 58: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

Thread Name:

Extended Properties:

----------------------------------------

Your file should look similar to the above output.

13. Close the application and Visual Studio.

To check the finished solution, open the EnoughPI.sln file.

Exercise 2: Create and use a custom Trace Listener

In this exercise you will build a custom Trace Listener to send formatted log entries to the Console standard output. You will then add this new Trace Listener to the EnoughPI application, and monitor the log entries in real-time.

First step

1. Open the EnoughPI.sln file.

Create a custom Trace Listener

1. Select the EnoughPI.Logging project. Select the Project | Add Reference… menu command. Select the Browse tab and select the following assemblies located here.

• Microsoft.Practices.EnterpriseLibrary.Common.dll, and • Microsoft.Practices.EnterpriseLibrary.Logging.dll.

2. Select the TraceListeners\ConsoleTraceListener.cs file in the Solution Explorer. Select the View | Code menu command. Add the following namespaces:

using Microsoft.Practices.EnterpriseLibrary.Common.Configuration; using Microsoft.Practices.EnterpriseLibrary.Logging; using Microsoft.Practices.EnterpriseLibrary.Logging.Configuration; using Microsoft.Practices.EnterpriseLibrary.Logging.Formatters; using Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners;

3. Add the following code to the ConsoleTraceListener class.

Document made for self preparation (Abhishek Maitrey) 58

Page 59: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

[ConfigurationElementType(typeof(CustomTraceListenerData))] public class ConsoleTraceListener : CustomTraceListener { public ConsoleTraceListener() : base() { }

public override void TraceData(TraceEventCache eventCache,

string source, TraceEventType eventType, int id, object data) { if (data is LogEntry && this.Formatter != null) { this.WriteLine(this.Formatter.Format(data as LogEntry)); } else { this.WriteLine(data.ToString()); } }

public override void Write(string message) { Console.Write(message); }

public override void WriteLine(string message) { // Delimit each message Console.WriteLine((string)this.Attributes["delimiter"]);

Document made for self preparation (Abhishek Maitrey) 59

Page 60: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

// Write formatted message Console.WriteLine(message); } }

Note: The base class is CustomTraceListener, which mandates that you override two abstract methods - Write(string message) and WriteLine(string message). However to format the message we need to override the TraceData method.

The ConsoleTraceListener is expecting a parameter, "delimiter", as part of the listener configuration.

4. Copy the output assembly to [Enterprise install directory]\bin.

Select the EnoughPI.Logging project. Select the Project | EnoughPI.Logging Properties… menu command, and select the Build Events tab. Add the following command as the Post-build event command line.

copy "$(TargetPath)" "..\..\..\..\..\..\..\..\..\bin"

Document made for self preparation (Abhishek Maitrey) 60

Page 61: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

The Enterprise Library Configuration tool will automatically load assemblies in the same directory.

5. Select the File | Save All menu command. 6. Select the Build | Build Solution menu command, to compile the

complete solution.

Using a custom TraceListener

1. Select the EnoughPI project App.config file in the Solution Explorer. Select the View | Open With… menu command. Select Enterprise Library Configuration and click the OK button.

2. Select the Logging Application Block | Trace Listeners node. Select the Action | New | Custom Trace Listener menu command.

Document made for self preparation (Abhishek Maitrey) 61

Page 62: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

3. Set the following property:

• Formatter = Text Formatter. 4. Select the Type property, and click the ellipses to display the Type

Selector dialog.

5. Select the ConsoleTraceListener class from the EnoughPI.Logging assembly and click the Ok button.

Document made for self preparation (Abhishek Maitrey) 62

Page 63: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

The Type Selector lists class, from assemblies in the same directory as the Enterprise Library Configuration tool, which inherit from CustomTraceListener and have a ConfigurationElementType of CustomTraceListenerData.

6. Select the Attributes property and click the ellipses to display the EditableKeyValue Collection Editor.

7. Click the Add button to add a new key/value pair.

• Key = delimiter, and • Value = "---------------------------"

Document made for self preparation (Abhishek Maitrey) 63

Page 64: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

Click the Ok button.

Note: You will remember our ConsoleTraceListener is expecting a parameter named delimiter, which is printed before each formatted log entry is written to the console.

8. Select the Logging Application Block | Category Sources | General node. Select the Action | New | Trace Listener Reference menu command.

Document made for self preparation (Abhishek Maitrey) 64

Page 65: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

9. Set the following property:

• ReferencedTraceListener = Custom TraceListener.

Document made for self preparation (Abhishek Maitrey) 65

Page 66: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

10. Select the File | Save All menu command, and close the Enterprise Library Configuration tool.

Note: If you don't close the Enterprise Library Configuration tool you will get a build error next time you build the EnoughPI.Logging post build event will fail.

View the TraceListener output

1. Select the EnoughPI project. Select the Project | EnoughPI Properties… menu command, and select the Application tab and set:

• Output type = Console Application.

2. Select the File | Save All menu command. 3. Select the Debug | Start Without Debugging menu command to

run the application. Enter your desired precision and click the Calculate button. The log entries will be displayed in the applications Console Window.

Document made for self preparation (Abhishek Maitrey) 66

Page 67: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

To check the finished solution, open the EnoughPI.sln file.

Exercise 3: Create and use a custom LogFormatter

In this exercise you will add a custom LogFormatter to a logging application.

First step

1. Open the EnoughPI.sln file.

Create a custom log formatter

1. Select the Formatters\XmlFormatter.cs file in the Solution Explorer. Select the View | Code menu command. Add the following namespaces:

using Microsoft.Practices.EnterpriseLibrary.Common.Configuration; using Microsoft.Practices.EnterpriseLibrary.Logging; using Microsoft.Practices.EnterpriseLibrary.Logging.Configuration; using Microsoft.Practices.EnterpriseLibrary.Logging.Formatters;

2. Add the following code to the XmlFormatter class.

Document made for self preparation (Abhishek Maitrey) 67

Page 68: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

[ConfigurationElementType(typeof(CustomFormatterData))] public class XmlFormatter : LogFormatter { private NameValueCollection Attributes = null;

public XmlFormatter(NameValueCollection attributes) { this.Attributes = attributes; }

public override string Format(LogEntry log) { string prefix = this.Attributes["prefix"]; string ns = this.Attributes["namespace"];

using (StringWriter s = new StringWriter()) { XmlTextWriter w = new XmlTextWriter(s); w.Formatting = Formatting.Indented; w.Indentation = 2;

w.WriteStartDocument(true); w.WriteStartElement(prefix, "logEntry", ns); w.WriteAttributeString("Priority", ns, log.Priority.ToString(CultureInfo.InvariantCulture)); w.WriteElementString("Timestamp", ns, log.TimeStampString); w.WriteElementString("Message", ns, log.Message); w.WriteElementString("EventId", ns, log.EventId.ToString(CultureInfo.InvariantCulture)); w.WriteElementString("Severity", ns, log.Severity.ToString()); w.WriteElementString("Title", ns, log.Title); w.WriteElementString("Machine", ns, log.MachineName); w.WriteElementString("AppDomain", ns, log.AppDomainName); w.WriteElementString("ProcessId", ns, log.ProcessId);

Document made for self preparation (Abhishek Maitrey) 68

Page 69: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

w.WriteElementString("ProcessName", ns, log.ProcessName); w.WriteElementString("Win32ThreadId", ns, log.Win32ThreadId); w.WriteElementString("ThreadName", ns, log.ManagedThreadName); w.WriteEndElement(); w.WriteEndDocument();

return s.ToString(); } } }

The log entry will be formatted as XML. Two parameters are expected (prefix, and namespace).

3. Select Build | Build Solution to compile the complete solution.

Use a custom log formatter

1. Select the EnoughPI project App.config file in the Solution Explorer. Select the View | Open With… menu command. Select Enterprise Library Configuration and click the OK button.

2. Select the Logging Application Block | Formatters node. Select the Action | New | Custom Formatter menu command. Set the following property:

• Name = Xml Formatter.

Document made for self preparation (Abhishek Maitrey) 69

Page 70: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

3. Select the Type property, and click the ellipses to display the Type Selector dialog.

4. Select the XmlFormatter class from the EnoughPI.Logging assembly and click the Ok button.

Document made for self preparation (Abhishek Maitrey) 70

Page 71: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

The Type Selector lists class, from assemblies in the same directory as the Enterprise Library Configuration tool, which inherit from LogFormatter and have a ConfigurationElementType of CustomFormatterData.

5. Select the Attributes property and click the ellipses to display the EditableKeyValue Collection Editor.

6. Add the following Key/Value pairs:

• Key = prefix, Value = x, and • Key = namespace, Value = EnoughPI/2.0

Click the Ok button.

Document made for self preparation (Abhishek Maitrey) 71

Page 72: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

Note: You will remember our XmlFormatter is expecting two parameters, the prefix and namespace for the XML document.

7. Select the Logging Application Block | Trace Listeners | Custom TraceListener node. Set the following property:

• Formatter = Xml Formatter.

Document made for self preparation (Abhishek Maitrey) 72

Page 73: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

8. Select the File | Save All menu command, and close the Enterprise Library Configuration tool.

9. Select the Debug | Start Without Debugging menu command to run the application. Enter your desired precision and click the Calculate button. The log entries will be displayed in the applications Console Window.

More information

1. For more information on how and when to instrument applications, including using custom request tracing to identify issues across multi-tiered applications, see "Using Advanced Microsoft .NET Performance 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 and also extends Enterprise Library to support request tracing across Web services, with a mechanism that would inter-operate with non-Microsoft technologies. The course also uses some freely available profiling tools to help solve performance issues once a bottleneck has been identified.

To check the finished solution, open the EnoughPI.sln file.

Copyright © 2005 Microsoft. All Rights Reserved.

Document made for self preparation (Abhishek Maitrey) 73

Page 74: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

Enterprise Library Hands On Labs

Lab 4: Exception Handling Application Block After completing this lab, you will be able to:

• Add Exception Logging to an application. • Use a Replace Handler to hide sensitive information.

Scenario

This lab demonstrates the use of the Enterprise Library Exception Handling Application Block.

Estimated Time To Complete this lab: 30 minutes.

Exercise 1: Logging Exceptions

In this exercise you will take an application without exception handling, and add local and global exception handlers that log the exceptions to the Event Log using the Exception Handling Application Block.

First step

1. Open the Puzzler.sln file.

Review the Application

1. This application performs two functions, it checks the spelling of words against a dictionary (unix dict for size) and it uses the dictionary to generate a list of words that can be constructed from a character list.

2. Select the Debug | Start Debugging menu command to run the application.

There is currently no exception handling in the application. Attempt to add a word which contains numbers to the dictionary (type "ab123" in

Document made for self preparation (Abhishek Maitrey) 74

Page 75: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

the word to check textbox and click Add Word). An unhandled exception will occur, which will break into the debugger.

Select the Debug | Stop Debugging menu command to exit the application and return to Visual Studio.

Add Try/Catch Exception Handling

1. Select the PuzzlerUI project. Select the Project | Add Reference … menu command. Select the Browse tab and select the following assembly located here.

• Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.dll.

While this assembly is the only assembly required for the ExceptionHandling API, other assemblies may need to be available in the bin\debug directory to provide specific exception handling functionality, which you will add later.

2. Select the Puzzler.cs file in the Solution Explorer. Select the View | Code menu command.

Add the following namespace inclusion at the top of the file:

using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;

3. Find the btnAddWord_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 { // TODO: Handle exceptions PuzzlerService.Dictionary.AddWord(txtWordToCheck.Text); errorProvider1.SetError(txtWordToCheck, ""); } catch (Exception ex)

Document made for self preparation (Abhishek Maitrey) 75

Page 76: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

{ bool rethrow = ExceptionPolicy.HandleException(ex, "UI Policy"); if (rethrow) throw;

MessageBox.Show(string.Format( "Failed to add word {0}, please contact support.", txtWordToCheck.Text)); } }

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 re-throw point, which is usually not the desired effect.

Enterprise Library Configuration Tool

1. Add a new Application configuration file (App.config) to the PuzzlerUI project.

Click on the PuzzlerUI project. Select the Project| Add New Item… menu command. Select Application configuration file template. Leave the Name as App.config.

Document made for self preparation (Abhishek Maitrey) 76

Page 77: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

2. We will use the Enterprise Library Configuration tool to configure our application. You may either start this tool from the Windows Start menu (select All Programs | Microsoft patterns and practices | Enterprise Library | Enterprise Library Configuration) and open the App.config file located here.

Alternatively you may configure Visual Studio to open the configuration file with the tool, as described below.

3. Select the App.config file in the Solution Explorer. Select the View | Open With… menu command. The OpenWith dialog is displayed. Click the Add button.

Document made for self preparation (Abhishek Maitrey) 77

Page 78: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

4. In the Add Program dialog, set the Program name to the EntLibConfig.exe file found here. Set the Friendly name to Enterprise Library Configuration. Click the OK button.

Visual Studio will pass the configuration file (App.config) to the EntLibConfig.exe program as a command line parameter.

5. In the Open With dialog, select the newly entered Enterprise Library Configuration program and click the OK button.

Document made for self preparation (Abhishek Maitrey) 78

Page 79: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

Configure the Application to Use Exception Management

1. Right click on the Application and select New | Exception Handling Application Block.

Document made for self preparation (Abhishek Maitrey) 79

Page 80: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

2. Select the Exception Handling Application Block node. Select the Action | New | Exception Policy menu command. Set following property:

• Name = UI Policy.

The policy name here must match that specified in the code. Typically you would use constants to help prevent typographical errors, especially since the exception handling typically is not tested as thoroughly as normal code paths.

3. Select the UI Policy node. Select the Action | New | Exception Type menu command.

This will open the Type Selector dialog. Select the System.Exception (the default) and click the Ok button.

Document made for self preparation (Abhishek Maitrey) 80

Page 81: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

This is a filter that specifies which exceptions should be processed by the exception handling code, and what handlers to run. In this case, all exceptions derived from System.Exception will be processed.

4. Set the following property for the System.Exception type:

• PostHandlingAction = None.

Document made for self preparation (Abhishek Maitrey) 81

Page 82: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

This will cause all exceptions to be handled internally within the exception handling code, and the caller will not be requested to re-throw the exception with its catch block.

5. Select the Exception Handling Application Block | UI Policy | Exception node. Select the Action | New | Logging Handler menu command.

Document made for self preparation (Abhishek Maitrey) 82

Page 83: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

Note: This automatically includes the Logging Application Block in your configuration.

6. Select the Exception Handling Application Block | UI Policy | Exception | Logging Handler node. Set the following properties:

• FormatterType = TextExceptionFormatter, and • LogCategory = General.

Document made for self preparation (Abhishek Maitrey) 83

Page 84: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

7. Select the File | Save All menu command to save the application configuration. Close the Enterprise Library Configuration tool.

Click on Yes in the dialog box warning about the app.config being modified outside the source editor.

Change the application to include the exception logging assembly

1. Select the PuzzlerUI project. Select the Project | Add Reference … menu command. Select the Browse tab and select the following assembly located here.

• Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging.dll.

Document made for self preparation (Abhishek Maitrey) 84

Page 85: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

Because one of the goals of the Enterprise Library is to keep the blocks de-coupled, it is possible to use the Exception Handling block without needing the Logging block (e.g. by creating your own exception handler). If you want to use the two blocks together, then you need to use this assembly, which contains an Exception Handler that logs via the logging block.

Run the Application

1. Select the Debug | Start Without Debugging menu command to run the application.

Try adding a number (type a number in the Word to check text box, and click Add Word) - a MessageBox is displayed with an error - "Failed to add word …, please contact support".

2. Check the Event Log by using the Event Viewer (Control Panel | Administrative Tools | Event Viewer). Look at the top of the Application Log. The exception will be logged.

Document made for self preparation (Abhishek Maitrey) 85

Page 86: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

After the previous exception the Exception Management Block will have written an entry in the Application Event Log using the Logging Block.

3. Close the application.

Add Global Exception Handling

1. While it is possible to put try/catch sections around all the event handlers in an application, it is usually better to provide a single generic handler that fires whenever an unhandled exception occurs within the application.

2. Select the Puzzler.cs file in the Solution Explorer. Select the View | Code menu command. Locate the btnAddWord_Click method, and remove the exception handling code added earlier.

Document made for self preparation (Abhishek Maitrey) 86

Page 87: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

private void btnAddWord_Click(object sender, System.EventArgs e) { PuzzlerService.Dictionary.AddWord(txtWordToCheck.Text); errorProvider1.SetError(txtWordToCheck, ""); }

3. Select the Startup.cs file in the Solution Explorer. Select the View | Code menu command. Add the following namespace inclusion at the top of the file:

Application execution begins here. There are two events you can use to listen for unhandled exceptions:

The Application.ThreadException event is raised when an unhandled exception occurs on the 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 the AppDomain.UnhandledException event will fire.

using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;

4. Add the following method to handle exceptions in the Startup class.

public static void HandleException(Exception ex, string policy) { Boolean rethrow = false; try { rethrow = ExceptionPolicy.HandleException(ex, policy); } catch (Exception innerEx) { string errorMsg = "An unexpected exception occured while " + "calling HandleException with policy '" + policy + "'. "; errorMsg += Environment.NewLine + innerEx.ToString();

Document made for self preparation (Abhishek Maitrey) 87

Page 88: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

MessageBox.Show(errorMsg, "Application Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);

throw ex; }

if (rethrow) { // WARNING: This will truncate the stack of the exception throw ex; } else { MessageBox.Show("An unhandled exception occurred and has " + "been logged. Please contact support."); } }

This method will use the exception handling block, and will also display valid information if there is a problem with the exception handling block itself (e.g. missing configuration).

It will also display a message to the user if the exception is swallowed (i.e. not re-thrown).

5. Add the following method as the event handler to the Application ThreadException event.

static void Application_ThreadException(object sender, ThreadExceptionEventArgs e) { HandleException(e.Exception, "UI Policy"); }

This event handler will use the policy (UI Policy) that you defined before for the UI layer. In the next exercise you will customize this policy to allow certain types of exception to "escape" and shut the application down.

Document made for self preparation (Abhishek Maitrey) 88

Page 89: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

6. Add an event handler for the AppDomain UnhandledException event.

static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { if (e.ExceptionObject is System.Exception) { HandleException((System.Exception)e.ExceptionObject, "Unhandled Policy"); } }

This handler will use a new policy, named Unhandled Policy, which will set up in the next exercise. The Unhandled Policy should almost always just log the exception, and not re-throw.

7. Connect the event handlers to the events at the beginning of the application by including the following code in the Main method (Inserted code in bold).

static void Main() { // TODO: Handle unhandled exceptions Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);

AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

Puzzler f = new Puzzler(); Application.Run(f); }

8. Select the Debug | Start Without Debugging menu command to run the application.

Document made for self preparation (Abhishek Maitrey) 89

Page 90: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

Try adding a number (type a number in the Word to check text box and click Add 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.

9. Close the application and Visual Studio.

To check the finished solution, open the Puzzler.sln file.

Exercise 2: Exception Handling Strategies

In this exercise you will secure part of our application service with Code Access Security and then use a Replace Handler with the Exception Handling Application Block to hide sensitive information from clients. You will also see how you can filter which exceptions escape the top application layer.

First step

1. Open the Puzzler2.sln file.

View the Service Modifications

1. Select the PuzzlerService project DictionaryService.cs file in the Solution Explorer. Select the View | Code menu command.

This class acts as a Service Interface on top of the Dictionary class. Within this class you provide exception filtering and transformation before sending the results back to the client. For techniques to intercept the call and automatically transform and handle exceptions, see the "Developing Microsoft .NET Service-Oriented Applications

®

" course of Mastering Industrial Strength .NET.

Protect the Service's 'Add Word' Functionality with Code Access Security

1. Select the PuzzlerService project Dictionary.cs file in the Solution Explorer. Select the View | Code menu command. Locate the AddWord method and decorate it with a security attribute as follows:

// TODO: Add security attribute [PrincipalPermission(SecurityAction.Demand, Role = "Grand PoohBah")]

Document made for self preparation (Abhishek Maitrey) 90

Page 91: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

public static Boolean AddWord(string wordToAdd) { if (!IsWord(wordToAdd)) { // It is not alphabetic! Throw an exception throw new ApplicationException( "Word to add does not consist of alphabetic letters"); } if (Dict[wordToAdd] == null) { Dict.Add(wordToAdd, wordToAdd); } return true; }

This method can now only be executed by a member of the role Grand PoohBah, an unlikely situation.

Note: Decorate the AddWord method in Dictionary.cs not DictionaryService.cs.

2. Select the Debug | Start Without Debugging menu command to 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 the service's AddWord function and throw a SecurityException, which you can check in the event viewer.

Document made for self preparation (Abhishek Maitrey) 91

Page 92: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

The SecurityException may be serialized from a Server to a Client (over Web Services) and contains information that may help an attacker break our security. You would prefer to catch and log the security exception on the server, then pass an exception containing less information to the client.

3. Close the application.

Configure the application to replace SecurityExceptions

1. Select the PuzzlerUI project App.config file in the Solution Explorer. Select the View | Open With… menu command. Select Enterprise Library Configuration and click the OK button.

The App.config file already has an empty policy, named Service Policy. By default, if a policy is empty, then exceptions will just be re-thrown inside the catch block, so in effect the policy will do nothing.

Document made for self preparation (Abhishek Maitrey) 92

Page 93: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

2. Select the Service Policy node. Select the Action | New | Exception Type menu command. Select the System.Security.SecurityException type (from mscorlib) and click the Ok button.

3. Select the Service Policy | SecurityException node and set the following property:

• PostHandlingAction = ThrowNewException.

Document made for self preparation (Abhishek Maitrey) 93

Page 94: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

4. Add a new Logging Handler. Select the Service Policy | SecurityException node. Select the Action | New | Logging Handler menu command. Set the following properties:

• FormatterType = TextExceptionFormatter, • LogCategory = General, and • Title = Security Exception in Service Layer.

Document made for self preparation (Abhishek Maitrey) 94

Page 95: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

5. Add a new Replace Handler. Select the Service Policy | SecurityException node. Select the Action | New | Replace Handler menu command. Set the following properties:

• ExceptionMessage = Unauthorized Access, and • ReplaceExceptionType =

System.Security.SecurityException (from mscorlib).

Document made for self preparation (Abhishek Maitrey) 95

Page 96: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

Although you have kept the type the same, by creating a new SecurityException, this will not provide the client any of the stack information or internal security exception information, which could compromise security.

Change the Application to Exit on a Security Exception

1. Select the UI Policy node. Select the Action | New | Exception Type menu command. Select the System.Security.SecurityException type (from mscorlib) and click the Ok button. Set the following property:

• PostHandlingAction = NotifyRethrow.

Document made for self preparation (Abhishek Maitrey) 96

Page 97: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

A PostHandlingAction of NotifyRethrow, will cause the handler for the Application.ThreadException event to re-throw the exception. The re-thrown exception will be an unhandled exception, and .NET will be forced to shut down the application.

2. Add a new Logging Handler to the SecurityException exception type under the UI Policy. Select the Action | New | Logging Handler menu command. Set the following properties:

• FormatterType = TextExceptionFormatter, • LogCategory = General, and • Title = Security Exception in UI Layer.

Document made for self preparation (Abhishek Maitrey) 97

Page 98: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

3. Select the File | Save All menu command to save the application configuration. Close the Enterprise Library Configuration tool.

Test the Replace Handler

1. Select the Debug | Start Without Debugging menu command to run the application.

Type a nonsense (alphabetic) word into the Word To Check textbox (ensure that you have an error flashing), then click on the Add Word button. If debugging click on Continue when it displays an "Unhandled exception" message. Open the event log to look at the events created. You can use the debugger to observe exactly what is happening and relate this to the Exception Handling Policies.

This time the Exception will be shown three times in the Event Log.

Document made for self preparation (Abhishek Maitrey) 98

Page 99: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

First a SecurityException 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 to the event log on the server (with all available information included) and then will capture a new replacement SecurityException (without specific stack information).

Second the replacing SecurityException 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 our case) and set the re-throw boolean to true which allows our code to decide to re-throw the second SecurityException.

Third the re-thrown SecurityException is caught by our AppDomain UnhandledException handler (it was thrown from outside the Application.Run) which applies Unhandled Policy. This will log the exception and display a MessageBox informing us that there was an error in the application.

The AppDomain UnhandledException handler does not consume exceptions, so the exception continues to pass to the runtime or debugger exception handler. This will cause a standard unhandled exception dialog box to be displayed.

To check the finished solution, open the Puzzler2.sln file.

Copyright © 2005 Microsoft. All Rights Reserved.

Document made for self preparation (Abhishek Maitrey) 99

Page 100: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

Enterprise Library Hands On Labs

Lab 5: Enterprise Library Cryptography Application Block After completing this lab, you will be able to:

• Keep non configuration data secret. • Store passwords in a secure manner.

Scenario

This lab demonstrates the use of the Enterprise Library Cryptography Block. Keeping secrets is both important and difficult. Probably the most common secrets you wish to keep are connection strings that often include usernames and passwords, however the task of securing these is handled for us by the Configuration Application Block and was discussed in the Data Access topic earlier. This lab looks at securing non configuration data and using hashing to secure passwords.

Estimated Time To Complete this lab: 30 minutes.

Exercise 1: Encrypt and Decrypt secrets

In this exercise you will look at protecting information that is not part of our configuration. As an example you will create an application that simulates a chat (Messenger) application and upgrade it to communicate using encrypted rather than plaintext strings.

First step

1. Open the ChatterBox.sln file.

Review the application

1. Select the Chat.cs file in the Solution Explorer. Select the View | Designer menu command.

Document made for self preparation (Abhishek Maitrey) 100

Page 101: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

The Chat form is used for sending and receiving instant messages. The top TextBox (gray color) displayed the conversation trace. The bottom TextBox (white color) is used to send new messages.

2. Select the Debug | Start Without Debugging menu command to run the application.

Two Chat forms will be opened (called Sam and Toby). Messages can be passed between these forms. Select the Toby window. Type some text into the Toby Says TextBox, and click the Send button. Repeat for the Sam window. Note, the conversation appears in both Chat forms.

Document made for self preparation (Abhishek Maitrey) 101

Page 102: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

A Console window is also displayed, to act as a conversation monitor. Every message is displayed in the Console window.

The messages are being sent between the windows as plaintext. You will use the Cryptography Block to encrypt and decrypt these communications using a symmetric key.

3. Close any application window to shutdown the application.

Adding Encryption and Decryption

1. Select the Project | Add Reference … menu command. Select the Browse tab and select the following assembly located here:

• Microsoft.Practices.EnterpriseLibrary.Security.Cryptography.dll. 2. Select the Chat.cs file in the Solution Explorer. Select the View |

Code menu command.

Document made for self preparation (Abhishek Maitrey) 102

Page 103: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

Add the following namespace inclusion to the list of namespaces at the top of the file:

using Microsoft.Practices.EnterpriseLibrary.Security.Cryptography;

3. Add the following code to the Chat class (added code in bold).

public partial class Chat : Form {

// TODO: Configuration symmetric algorithm provider name private const string symmProvider = "ChatProvider";

. . .

}

The constant must match a named symmetric crypto provider (see symmetricCryptoProviders section in the App.config later in the lab).

4. Modify the SendMessage method to encrypt the message using the Cryptographer (changes in bold).

private void SendMessage(string message) { // TODO: Encrypt message string encrypted = Cryptographer.EncryptSymmetric(symmProvider, message);

// Fire SendingMessage Event if (this.SendingMessage != null) this.SendingMessage(new MessageEventArgs(this._name, encrypted)); }

5. Modify the MessageReceived method to decrypt the message using the Cryptographer (changes in bold).

Document made for self preparation (Abhishek Maitrey) 103

Page 104: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

private void MessageReceived(MessageEventArgs args) { string message = args.Message;

// TODO: Decrypt message string plainText = Cryptographer.DecryptSymmetric(symmProvider, message);

this.txtMessages.AppendText( args.Sender + " says: " + plainText + Environment.NewLine); }

Enterprise Library Configuration

1. Add a new Application configuration file (App.config) to the ChatterBox project.

Select the ChatterBox project. Select the Project| Add New Item… menu command. Select Application configuration file template. Leave the Name as App.config.

Document made for self preparation (Abhishek Maitrey) 104

Page 105: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

2. We will use the Enterprise Library Configuration tool to configure our application. You may either start this tool from the Windows Start menu (select All Programs | Microsoft patterns and practices | Enterprise Library | Enterprise Library Configuration) and open the App.config file located here.

Alternatively you may configure Visual Studio to open the configuration file with the tool, as described below.

3. Select the App.config file in the Solution Explorer. Select the View | Open With… menu command. The OpenWith dialog is displayed. Click the Add button.

Document made for self preparation (Abhishek Maitrey) 105

Page 106: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

4. In the Add Program dialog, set the Program name to the EntLibConfig.exe file found here. Set the Friendly name to Enterprise Library Configuration. Click the OK button.

Visual Studio will pass the configuration file (App.config) to the EntLibConfig.exe program as a command line parameter.

5. In the Open With dialog, select the newly entered Enterprise Library Configuration program and click the OK button.

Document made for self preparation (Abhishek Maitrey) 106

Page 107: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

Configure the application to use Symmetric key Cryptography

1. Right click on the Application and select New | Cryptography Application Block.

Document made for self preparation (Abhishek Maitrey) 107

Page 108: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

2. Select the Cryptography Application Block | Symmetric Providers node. Select the Action | New | Symmetric Algorithm Provider menu command.

Document made for self preparation (Abhishek Maitrey) 108

Page 109: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

3. The Type Selector dialog (for SymmetricAlgorithm Type) will be displayed.

Select the RijndaelManaged Type and click the Ok button.

Document made for self preparation (Abhishek Maitrey) 109

Page 110: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

An encryption algorithm provides no security if the encryption algorithm is cracked or is vulnerable to brute force cracking. Custom algorithms are particularly vulnerable if they have not been tested. Instead, use published, well-known encryption algorithms that have withstood years of rigorous attacks and scrutiny.

Recommended key lengths change as computing power grows. Encryption key lengths that range from 128 bits to 256 bits are currently considered to be secure. Most modern algorithms use keys that are at least 128 bits long.

For symmetric algorithms, AES, also known as Rijndael, is recommended. This algorithm supports key lengths of 128, 192, 256 bits. The DES algorithm is not recommended.

4. The Cryptographic Key Wizard will start. Select the Create a new key option, and click the Next button.

Document made for self preparation (Abhishek Maitrey) 110

Page 111: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

The wizard will lead you through the process of creating and protecting a cryptographic key.

5. Click the Generate button to generate a new key. Click the Next button.

Your generated key will no doubt be different to the example shown in the above picture.

6. Select the Ellipsis (…) button and choose a key file location. For this lab, the Windows Desktop may prove to be a convenient location. Select a file name (e.g. ChatterBox.key). Click the Next button.

Document made for self preparation (Abhishek Maitrey) 111

Page 112: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

The key is no longer stored in the configuration file. Each key is stored in a separate file that is protected with DPAPI.

7. Select User mode, or Machine mode, and click the Finish button.

When you create the key, you choose either machine mode or user mode to limit access to the key.

Document made for self preparation (Abhishek Maitrey) 112

Page 113: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

Use machine mode in the following situations:

• Your application runs on its own dedicated server with no other applications.

• You have multiple applications that run on the same server and you want those applications to be able to share sensitive information.

Use user mode if you run your application in a shared hosting environment and you want to make sure that your application's sensitive data is not accessible to other applications on the server. In this situation, each application should run under a separate identity, and the resources for the application—such as files and databases—should be restricted to that identity.

Note:

• If you use DPAPI with machine mode, the encrypted string is specific to a particular computer, so you must generate the encrypted data on every computer. Do not copy the encrypted data across computers that are in a server farm or a cluster.

8. Select the Cryptography Application Block | Symmetric Providers | RijndaelManaged node. Select the following property:

• Name = ChatProvider.

Document made for self preparation (Abhishek Maitrey) 113

Page 114: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

ChatProvider is the name of the symmetric provider we used earlier in the Chat.cs code.

9. Save the application configuration by selecting the File | Save All menu command.

10. Close the Enterprise Library Configuration tool. Click on Yes in the dialog box warning about the app.config being modified outside the source editor.

Run the application

1. Select the Debug | Start Without Debugging menu command.

Pass messages between Toby and Sam. The messages are passed encrypted (see the encrypted messages in the Console), but decrypted by the receiver.

Document made for self preparation (Abhishek Maitrey) 114

Page 115: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

2. You may or may not have noticed that the application has become a little less stable now that you are using Cryptographer (hint, try sending an empty string). you would normally add some code to check the strings before you try to encrypt or decrypt them. For example you should check for zero length strings in both the SendMessage and MessageReceived methods. The MessageReceived method should also check that the strings you are about to decrypt are a multiple of 4 bytes long and only contain valid Base64 characters. These checks have been omitted for clarity.

3. Close the application.

Add error handling

1. The best way to handle exceptions is to make sure that they don't happen in the first place with some guard code. Let's first make sure that you don't try to encrypt a zero length string.

Document made for self preparation (Abhishek Maitrey) 115

Page 116: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

Select the Chat.cs file in the Solution Explorer. Select the View | Code menu command. Add the following code to the SendMessage method.

private void SendMessage(string message) { if ((message != null) && (message.Trim().Length > 0))

{ // TODO: Encrypt message string encrypted = Cryptographer.EncryptSymmetric(symmProvider, message);

// Fire SendingMessage Event if (this.SendingMessage != null) this.SendingMessage(new MessageEventArgs(this._name, encrypted)); } }

To check the finished solution, open the ChatterBox.sln file.

Exercise 2: Use a HashProvider to store a one-way hashed password

In this exercise you will use a one-way hashing algorithm to encrypt a password stored in an XML file.

First step

1. Open the UserUI.sln file.

Review the application

1. Select the Debug | Start Without Debugging menu command to run the application.

2. The application allows you to manage usernames and passwords in an XML configuration file.

Document made for self preparation (Abhishek Maitrey) 116

Page 117: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

Add a new user named, Elmo. Click the New User button. Enter the name Elmo, and leave the passwords as the default (P@ssw0rd). Click the Ok button.

Repeat for new user, Zoe.

3. Click the Save button to save your changes to the UserStore.config file.

4. Click the Close button to shutdown the application. 5. Select the UserStore.config (in the UserUI project) file in the

Solution Explorer. Select the View | Open menu command.

You can see the password in plain text, not what you would like.

<?xml version="1.0" encoding="utf-8"?> <configuration> <configSections> <section name="userStore"

Document made for self preparation (Abhishek Maitrey) 117

Page 118: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

type="UserStore.Configuration.UserSettings, UserStore, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> </configSections> <userStore> <users> <add name="Elmo" password="P@ssw0rd" /> <add name="Zoe" password="P@ssw0rd" /> </users> </userStore> </configuration>

It is not necessarily a good idea to encrypt passwords using a symmetric key like the one you used in the previous exercise because if the key is compromised, so are all our passwords.

One-way hashing algorithms are usually used with passwords. You would hash the password before comparing it with a hashed password stored somewhere (usually in a database). This way even if the database is compromised our passwords are still safe.

Adding extra information 'Salt' to the password before hashing also makes the password much harder to crack. This is the default with the Cryptography Block's hashing providers.

Configure the Hash Provider

1. Select the App.config (in the UserUI project) in the Solution Explorer. Select the View | Open With… menu command. Select Enterprise Library Configuration and click the OK button.

2. The application configuration already has two Configuration Sources defined. The application is using the Enterprise Library wrapper classes for Configuration to manage the UserStore.config location and contents.

Document made for self preparation (Abhishek Maitrey) 118

Page 119: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

3. Right click on the application configuration file and select the New | Cryptography Application Block menu command.

Document made for self preparation (Abhishek Maitrey) 119

Page 120: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

4. Select the Cryptography Application Block | Hash Providers node. Select the Action | New | HashAlgorithm Provider menu command.

Document made for self preparation (Abhishek Maitrey) 120

Page 121: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

5. The Type Selector dialog (for HashAlgorithm Type) will be displayed.

Select the SHA1Managed Type and click the Ok button.

Document made for self preparation (Abhishek Maitrey) 121

Page 122: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

For hashing algorithms, the SHA256Managed algorithm is recommended. This algorithm uses a hash size of 256 bits. The hash size of SHA1Managed hashing algorithm is 160 bits. This algorithm is acceptable but not encouraged. The MD4 and MD5 algorithms are no longer recommended.

6. Select the Cryptography Application Block | Hash Providers | SHA1Managed node. Select the following property:

• Name = PasswordHasher, and • SaltEnabled = True.

Document made for self preparation (Abhishek Maitrey) 122

Page 123: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

7. Select the File | Save All menu command to save the application configuration. Close the Enterprise Library Configuration Console.

Use Hash Provider

1. Select the UserStore project in the Solution Explorer. Select the Project | Add Reference … menu command. Select the Browse tab and select the following assembly located here:

• Microsoft.Practices.EnterpriseLibrary.Security.Cryptography.dll. 2. Select the Security | HashHelper.cs file (in the UserStore project)

in the Solution Explorer. Select the View | Code menu command.

Add the following namespace inclusion:

using Microsoft.Practices.EnterpriseLibrary.Security.Cryptography;

Document made for self preparation (Abhishek Maitrey) 123

Page 124: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

3. Add the following code to the HashHelper class.

class HashHelper { private HashHelper() { }

// TODO: Hash provider name must match app.config private const string hashProvider = "PasswordHasher";

. . .

}

The constant must match a named Hash provider (see hashProviders section in the App.config file).

4. Modify the following code in the CreateHash method.

public static string CreateHash(string plainText) { string hash = null;

// TODO: Hash the plain text hash = Cryptographer.CreateHash(hashProvider, plainText);

return hash; }

Keep in mind that sensitive data should be cleared in memory as soon as possible. Leaving sensitive data unencrypted in memory can expose the data to security risks. You should note that data in memory may also end up on the hard disk, because the operating system can write data to a swap file. Also, if the computer crashes, the operating system can dump the contents of memory to disk.

5. Select the Debug | Start Without Debugging menu command to run the application.

Document made for self preparation (Abhishek Maitrey) 124

Page 125: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

6. Reset the password for Elmo. Click the Reset Password button. Leave the passwords as the default (P@ssw0rd). Click the Ok button.

Repeat for new user, Zoe.

You must replace the existing plain text password in the UserStore.config file with a password hash.

7. Click the Save button to save your changes to the UserStore.config file.

8. Attempt to change Elmo's password, which will validate the existing password. Click the Change Password button. The Old Password is P@ssw0rd (the default). Use any new password you like (not zero length) and click the Ok button.

Why does the validation of the existing password fail? Explanation later.

Document made for self preparation (Abhishek Maitrey) 125

Page 126: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

9. Click the Close button to shutdown the application. 10. Select the UserStore.config (in the UserUI project) file in the

Solution Explorer. Select the View | Open menu command.

You can see the password has been hashed.

Notice the hashing is different in each case, even though the actual password is the same. The difference is due to the addition of the 'Salt'. Hence, to test the validity of a password we cannot simply reapply the hashing to the plain text password and string compare to two hashed values. This is why the Change Password failed earlier.

11. Select the Security | HashHelper.cs file (in the UserStore project) in the Solution Explorer. Select the View | Code menu command.

Modify the following code in the CompareHash method.

public static bool CompareHash(string plainText, string hashedText) { bool compare = false;

// TODO: Compare plain text with hash compare = Cryptographer.CompareHash(hashProvider, plainText, hashedText);

return compare; }

12. Select the Debug | Start Without Debugging menu command to run the application.

13. Change Elmo's password, which will validate the existing password. Click the Change Password button. The Old Password is P@ssw0rd (the default). Use any new password you like (not zero length) and click the Ok button.

Document made for self preparation (Abhishek Maitrey) 126

Page 127: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

The password is changed successfully.

More information

1. 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 Security Techniques"® course which is part of the Mastering Industrial Strength .NET series.

To check the finished solution, open the UserUI.sln file.

Copyright © 2005 Microsoft. All Rights Reserved.

Document made for self preparation (Abhishek Maitrey) 127

Page 128: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

Enterprise Library Hands On Labs

Lab 6: Security Application Block - Hands On Lab After completing this lab, you will be able to:

• To use ASP.NET Membership for application authentication and authorization.

• To use the Enterprise Library Security block to add rule-based authorization.

Scenario

This lab demonstrates the use of the Enterprise Library, Security Application Block.

Estimated Time To Complete this lab: 30 minutes.

Exercise 1: Secure an Application

In this exercise you will add authentication and role based authorization to an existing application. You will configure application users and roles via the ASP.NET Membership API.

First step

1. Open the BugSmak.sln file.

Adding authentication to the application

1. Select the Debug | Start Without Debugging menu command to run the application. The application as yet is unable to authenticate users.

The application will be configured to use the ASP.NET Membership API for authentication (login security).

Document made for self preparation (Abhishek Maitrey) 128

Page 129: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

2. Close the application. 3. Select the Security \ SecurityHelper.cs file in the Solution

Explorer. Select the View | Code menu command.

Add the following namespace inclusion to the list of namespaces at the top of the file:

using System.Web.Security;

4. Add the following code to the Authenticate method.

public static bool Authenticate(string username, string password) { bool authenticated = false;

// TODO: Authenticate Credentials authenticated = Membership.ValidateUser(username, password);

// TODO: Get Roles

return authenticated; }

The Authenticate method is called by the LoginForm to validate the user credentials. The Membership.ValidateUser method is called to perform the validation.

The membership system uses a provider model so that the application is not tied to a particular data store. ASP.NET ships with two membership providers: one that uses Microsoft SQL Server as a data source and another that uses Windows Active Directory.

Document made for self preparation (Abhishek Maitrey) 129

Page 130: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

It is also possible to create a custom membership provider, which we have done to read the application members from an XML file.

5. Select the Security | Providers | ReadOnlyXmlMembershipProvider.cs file in the Solution Explorer. Select the View | Code menu command and review the code.

The ReadOnlyXmlMembershipProvider (inherits from MembershipProvider) is an example of a custom membership provider. This implementation, which reads an unencrypted XML file is not meant as an example of good practice, but useful for this laboratory exercise.

6. Select the App.config file in the Solution Explorer. Select the View | Open menu command.

Review the membership provider configuration. The authentication data store is identified as the Users.xml file.

<?xml version="1.0" encoding="utf-8"?> <configuration> <system.web> <membership defaultProvider="ReadOnlyXmlMembershipProvider"> <providers> <add name="ReadOnlyXmlMembershipProvider" type="BugSmak.Security.Providers.ReadOnlyXmlMembershipProvider, BugSmak" description="Read-only XML membership provider" xmlFileName="Users.xml" /> </providers> </membership> . . .

</system.web> </configuration>

Once you have a custom membership provider, you can configure your application to use that provider in the same way that you configure the

Document made for self preparation (Abhishek Maitrey) 130

Page 131: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

application to use an ASP.NET provider. The Membership class will automatically invoke your custom provider to communicate with your authentication data source.

7. Select the Users.xml file in the Solution Explorer. Select the View | Open menu command.

The following users have been pre-defined.

Username Password Role(s) Tom P@ssw0rd Employee Dick P@ssw0rd Developer Harry P@ssw0rd Manager

.

8. Select the Debug | Start Without Debugging menu command to run the application.

Sign-in as Tom, Dick or Harry to confirm the correct setup of the membership provider.

Select the File | Sign Out menu command and attempt to sign in with an incorrect username or password.

Document made for self preparation (Abhishek Maitrey) 131

Page 132: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

The Password field has been initialized to P@ssw0rd (see LoginForm.cs) to save you repeatedly typing the password during the laboratory exercise.

Notes:

• The application is the skeleton of a simple bug tracking system. The application has a skeleton for the following functions: Raise a Bug, Assign a Bug to a developer for resolution, and Resolve a Bug.

• The password is case sensitive, but the username is not. 9. Sign-in as Tom. Select the Tasks | Raise New Bug menu

command. You are confronted with a message "Sorry, you aren't allowed to access that form". Similarly, attempt to open the other tasks (Assign Bug, and Resolve Bug).

10. Close the application.

Adding role-base authorization

1. Select the TaskForms \ RaiseBug.cs file in the Solution Explorer. Select the View | Code menu command.

Review the PrincipalPermissions.

The RaiseBug form demands that the user have any of the Developer, Employee, or Manager roles. Attempting to run the form without the necessary authorization results in a SecurityException which is caught by the MainForm (see MainForm.cs).

The following role requirements are enforced via PrincipalPermissions:

TaskForm Role Required RaiseBug Employee, Developer, or Manager AssignBug Manager ResolveBug Developer or Manager

Document made for self preparation (Abhishek Maitrey) 132

Page 133: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

We have implemented authentication, but have not determined the user roles. The application will be configured to use a membership RoleProvider to retrieve user roles later in this section of the lab.

2. Select the Security \ SecurityHelper.cs file in the Solution Explorer. Select the View | Code menu command.

Add the following code to the Authenticate method.

public static bool Authenticate(string username, string password) { bool authenticated = false;

// TODO: Authenticate Credentials authenticated = Membership.ValidateUser(username, password);

// TODO: Get Roles if (!authenticated) return false;

IIdentity identity; identity = new GenericIdentity(username, Membership.Provider.Name);

string[] roles = Roles.GetRolesForUser(identity.Name); IPrincipal principal = new GenericPrincipal(identity, roles);

// Place user's principal on the thread Thread.CurrentPrincipal = principal;

Document made for self preparation (Abhishek Maitrey) 133

Page 134: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

return authenticated; }

The roles are retrieved from the Users.xml file via a custom RoleProvider (ReadOnlyXmlRoleProvider.cs), and a new principal created which contains the users identity and roles.

To use the authenticated principal in our application we place it on the Thread.

3. Select the App.config file in the Solution Explorer. Select the View | Open menu command.

Review the role manager provider configuration. The data store is identified as the Users.xml file.

<?xml version="1.0" encoding="utf-8"?> <configuration> <system.web> . . .

<roleManager enabled="true" defaultProvider="ReadOnlyXmlRoleProvider"> <providers> <add name="ReadOnlyXmlRoleProvider" type="BugSmak.Security.Providers.ReadOnlyXmlRoleProvider, BugSmak" description="Read-only XML role provider" xmlFileName="Users.xml" /> </providers> </roleManager> </system.web> </configuration>

4. Select the Debug | Start Without Debugging menu command to run the application.

Document made for self preparation (Abhishek Maitrey) 134

Page 135: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

Sign-in as Tom, Dick and Harry in turn to confirm the correct setup of the role provider.

User Task Access Tom (Employee) Raise New Bug

Dick (Developer) Raise New Bug, and Resolve Bug

Harry (Manager) Raise New Bug, Resolve Bug, and Assign Bug.

.

To check the finished solution, open the BugSmak.sln file.

Exercise 2: Use Rule-based Authorization In an Application

In this exercise you will use an AuthorizationProvider to secure application tasks. In the previous exercise you secured an application using role based authorization. Role based authorization can be difficult to configure as roles do not always neatly map to tasks or use cases. You will use the AuthorizationProvider to:

• map task based authorization to complex combinations of roles, and • extract authorization from the application code.

First step

1. Open the BugSmak.sln file.

Enterprise Library Configuration

1. We will use the Enterprise Library Configuration tool to configure our application. You may either start this tool from the Windows Start menu (select All Programs | Microsoft patterns and practices | Enterprise Library | Enterprise Library Configuration) and open the App.config file located here.

Document made for self preparation (Abhishek Maitrey) 135

Page 136: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

Alternatively you may configure Visual Studio to open the configuration file with the tool, as described below.

2. Select the App.config file in the Solution Explorer. Select the View | Open With… menu command. The OpenWith dialog is displayed. Click the Add button.

3. In the Add Program dialog, set the Program name to the EntLibConfig.exe file found here. Set the Friendly name to Enterprise Library Configuration. Click the OK button.

Document made for self preparation (Abhishek Maitrey) 136

Page 137: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

Visual Studio will pass the configuration file (App.config) to the EntLibConfig.exe program as a command line parameter.

4. In the Open With dialog, select the newly entered Enterprise Library Configuration program and click the OK button.

Adding Authorization Rules with the Enterprise Library Configuration Tool

1. Right click on the Application and select New | Security Application Block.

Document made for self preparation (Abhishek Maitrey) 137

Page 138: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

2. Add a new Authorization Rule Provider. Select the Security Application Block | Authorization node. Select the Action | New | Authorization Rule Provider menu command.

Document made for self preparation (Abhishek Maitrey) 138

Page 139: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

3. Set the following property:

• Name = BugSmak Rules.

Document made for self preparation (Abhishek Maitrey) 139

Page 140: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

4. Select the Security Application Block | Authorization | BugSmak Rules node. Select the Action | New | Rule menu command.

Document made for self preparation (Abhishek Maitrey) 140

Page 141: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

5. Open the Rule Expression Editor by clicking the ellipsis in the Expression property.

6. Set the following properties:

• Rule Name = Raise Bug, and • Expression = R:Developer OR R:Employee OR R:Manager.

Click the OK button.

Document made for self preparation (Abhishek Maitrey) 141

Page 142: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

The user must be in either Developer, Employee, or Manager role.

7. Add the following New Rules and Expressions:

Rule Name Expression Raise Bug ** R:Developer OR R:Employee OR R:Manager Assign Bug R:Manager Resolve Bug R:Developer OR R:Manager

** Already entered in previous step.

Document made for self preparation (Abhishek Maitrey) 142

Page 143: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

8. Select the Security Application Block node and set the following property:

• DefaultAuthorizationInstance = BugSmak Rules.

Document made for self preparation (Abhishek Maitrey) 143

Page 144: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

9. Select the File | Save All menu command to save the configuration. Close the Enterprise Library Configuration tool.

Adding Task Based Authorization

1. Select the TaskForms \ RaiseBug.cs file in the Solution Explorer. Select the View | Code menu command.

The form no longer uses the hard-coded PrincipalPermissions (commented-out). Rather the more flexible task authorization is checked before the object is constructed.

AssignBug.cs and ResolveBug.cs have similar code.

//[PrincipalPermission(SecurityAction.Demand, Role = "Employee")] //[PrincipalPermission(SecurityAction.Demand, Role = "Developer")] //[PrincipalPermission(SecurityAction.Demand, Role = "Manager")]

Document made for self preparation (Abhishek Maitrey) 144

Page 145: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

public static RaiseBug Create() { // TODO: Check Authorization if (!SecurityHelper.Authorized(AuthRule.Raise)) { throw new SecurityException(); }

return new RaiseBug(); }

Notes:

• For compile-time checking, the rule names (e.g. "Raise Bug") are mapped to constants in the Constants.cs file.

2. Select the Project | Add Reference … menu command. Select the Browse tab and select the following assembly located here.

• Microsoft.Practices.EnterpriseLibrary.Security.dll. 3. Select the Security \ SecurityHelper.cs file in the Solution

Explorer. Select the View | Code menu command.

Add the following namespace inclusion to the list of namespaces at the top of the file:

using Microsoft.Practices.EnterpriseLibrary.Security;

4. Add the following code to Authorized method.

public static bool Authorized(string rule) { bool authorized = false;

// TODO: Check rule-base authorization IAuthorizationProvider ruleProvider;

Document made for self preparation (Abhishek Maitrey) 145

Page 146: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

ruleProvider = AuthorizationFactory.GetAuthorizationProvider();

authorized = ruleProvider.Authorize(Thread.CurrentPrincipal, rule);

return authorized; }

Use the AuthorizationFactory to retrieve the default authorization provider. The authorization provider will test the current principal (roles and identity) against the rule.

5. Select the Debug | Start Without Debugging menu command to run the application.

Sign-in as Tom, Dick and Harry in turn to confirm the correct setup of the role provider.

User Task Access Tom (Employee) Raise New Bug

Dick (Developer) Raise New Bug, and Resolve Bug

Harry (Manager) Raise New Bug, Resolve Bug, and Assign Bug.

.

6. Close the application.

More information

1. For more information on how to implement security in distributed applications, see the "Using Advanced Microsoft .NET Application Security Techniques

®

" course as part of the Mastering Industrial Strength .NET series. This uses Enterprise Library to incorporate

Document made for self preparation (Abhishek Maitrey) 146

Page 147: Enterprise Library Hands on Labs

Enterprise Library for .NET Framework 2.0 Hands On Labs

security in distributed solutions, including building your own security providers and integrating with external authorization systems (e.g. authorization manager).

To check the finished solution, open the BugSmak.sln file.

Copyright © 2005 Microsoft. All Rights Reserved.

Document made for self preparation (Abhishek Maitrey) 147