แชร์ผ่าน


Chapter 1 - Welcome to the Library

Meet the Librarian

Before we begin our exploration of Microsoft® Enterprise Library and the wondrous range of capabilities and opportunities it encompasses, you need to meet the Librarian. Sometimes we call him Tom, sometimes we call him Chris, and sometimes we call him Grigori. But, despite this somewhat unnerving name variability, he—in collaboration with an advisory board of experts from the industry and other internal Microsoft product groups, and a considerable number of other community contributors—is the guardian and protector of the Microsoft Enterprise Library.

Since its inception as a disparate collection of individual application blocks, the Librarian has guided, prodded, inspired, and encouraged his team to transform it into a comprehensive, powerful, easy-to-use, and proven library of code that can help to minimize design and maintenance pain, maximize development productivity, and reduce costs. And now in version 5.0, it contains even more built-in goodness that should make your job easier. It's even possible that, with the time and effort you will save, Enterprise Library can reduce your golf handicap, help you master the ski slopes, let you spend more time with your kids, or just make you a better person. However, note that the author, the publisher, and their employees cannot be held responsible if you just end up watching more TV or discovering you actually have a life.

What You Get with Enterprise Library

Enterprise Library is made up of a series of application blocks, each aimed at managing specific crosscutting concerns. In case this concept is unfamiliar, crosscutting concerns are those annoying tasks that you need to accomplish in several places in your application. When trying to manage crosscutting concerns there is often the risk that you will implement slightly different solutions for each task at each location in your application, or that you will just forget them altogether. Writing entries to a system log file or Windows® Event Log, caching data, and validating user input are typical crosscutting concerns. While there are several approaches to managing them, the Enterprise Library application blocks make it a whole lot easier by providing generic and configurable functionality that you can centralize and manage.

Note

What are application blocks? The definition we use is "pluggable and reusable software components designed to assist developers with common enterprise development challenges." Application blocks help address the kinds of problems developers commonly face from one line-of-business project to the next. Their design encapsulates the Microsoft recommended practices for Microsoft .NET Framework-based applications, and developers can add them to .NET-based applications and configure them quickly and easily.

As well as the application blocks, Enterprise Library contains configuration tools, plus a set of core functions that manage tasks applicable to all of the blocks. Some of these functions—routines for handling configuration and serialization, for example—are exposed and available for you to use in your own applications.

And, on the grounds that you need to learn how to use any new tool that is more complicated than a hammer or screwdriver, Enterprise Library includes a range of sample applications, descriptions of key scenarios for each block, hands-on labs, and comprehensive reference documentation. You even get all of the source code and the unit tests that the team created when building each block (the team follows a test-driven design approach by writing tests before writing code). So you can understand how it works, see how the team followed good practices to create it, and then modify it if you want it to do something different. Figure 1 shows the big picture for Enterprise Library.

Figure 1

Enterprise Library—the big picture

Ff953191.d1ad2293-a6d9-4a03-94b1-641c6e85a079-thumb(en-us,PandP.50).png

Things You Can Do with Enterprise Library

If you look at the installed documentation, you'll see that Enterprise Library today actually contains nine application blocks. However, there are actually only seven blocks that "do stuff"—these are referred to as functional blocks. The other two are concerned with "wiring up stuff" (the wiring blocks). What this really means is that there are seven blocks that target specific crosscutting concerns such as caching, logging, data access, and validation. The other two, the Unity Dependency Injection mechanism and the Policy Injection Application Block, are designed to help you implement more loosely coupled, testable, and maintainable systems. There’s also some shared core pieces used in all the blocks. This is shown in Figure 2.

Figure 2

The parts of Enterprise Library

Ff953191.95938d7e-8264-4158-9706-dc23d075adf7-thumb(en-us,PandP.50).png

In this book we'll be concentrating on the seven functional blocks. If you want to know more about how you can use Unity and the Policy Injection Application Block, check out the appendices for this guide. They describe the capabilities of Unity as a dependency injection mechanism and the use of policy injection in more detail.

The following list describes the crosscutting scenarios you'll learn about in this book:

  • Caching. The Caching Application Block lets you incorporate a local cache in your applications that uses an in-memory cache and, optionally, a database or isolated storage backing store. The block provides all the functionality needed to retrieve, add, and remove cached data, and supports configurable expiration and scavenging policies. You can also extend it by creating your own pluggable providers or by using third-party providers—for example, to support distributed caching and other features. Caching can provide considerable improvements in performance and efficiency in many application scenarios.
  • Credential Management. The Security Application Block lets you easily implement common authorization-related functionality, such as caching the user's authorization and authentication data and integrating with the Microsoft .NET Framework security features.
  • Data Access. The Data Access Application Block simplifies many common data access tasks such as reading data for display, passing data through application layers, and submitting changed data back to the database system. It includes support for both stored procedures and in-line SQL, can expose the data as a sequence of objects for client-side querying, and provides access to the most frequently used features of ADO.NET in simple-to-use classes.
  • Encryption. The Cryptography Application Block makes it easy to incorporate cryptographic functionality such as encrypting and decrypting data, creating a hash from data, and comparing hash values to verify that data has not been altered. Using this block can help you avoid common pitfalls when developing custom mechanisms that might introduce security vulnerabilities.
  • Exception Handling. The Exception Handling Application Block lets you quickly and easily design and implement a consistent strategy for managing exceptions that occur in various architectural layers of your application. It can log exception information, hide sensitive information by replacing the original exception with another exception, and maintain contextual information for an exception by wrapping the original exception inside another exception.
  • Logging. The Logging Application Block simplifies the implementation of common logging functions such as writing information to the Windows Event Log, an e-mail message, a database, Windows Message Queuing, a text file, a Windows Management Instrumentation (WMI) event, or a custom location.
  • Validation. The Validation Application Block provides a range of features for implementing structured and easy-to-maintain validation mechanisms using attributes and rule sets, and integrating with most types of application interface technologies.

Why You Should Use Enterprise Library

As you can see from the previous section, Enterprise Library provides a comprehensive set of features that can help you to manage your crosscutting concerns though a reusable set of components and core functionality. Of course, like many developers, you may suffer from the well-known NIH (not invented here) syndrome. But, seriously, isn’t it about time that every developer on your team stopped writing his or her own logging framework? It's a commonly accepted fact that the use of standard and proven code libraries and components can save development time, minimize costs, reduce the use of precious test resources, and decrease the overall maintenance effort. In the words of the Librarian, "These days you cannot afford not to reuse."

Note

You can download the Nucleus Research 2009 Report on Microsoft patterns & practices, which reviews the key components, benefits, and includes direct feedback from software architects and developers who have adopted patterns & practices deliverables in their projects and products from https://msdn.microsoft.com/en-us/practices/ee406167.aspx.

And it's not as though Enterprise Library is some new kid on the block that might morph into something completely different next month. Enterprise Library as a concept has been around for many years, and has passed through five full releases of the library as well as intermediate incremental releases.

Enterprise Library continues to evolve along with the capabilities of the .NET Framework. As the .NET Framework has changed over time, some features that were part of Enterprise Library were subsumed into the core, while Enterprise Library changed to take advantage of the new features available in both the .NET Framework and the underlying system. Examples include new programming language capabilities and improved performance and capabilities in the .NET configuration and I/O mechanisms. Yet, even in version 5.0, the vast majority of the code is entirely backwards compatible with applications written to use Enterprise Library 2.0.

You can also use Enterprise Library as learning material—not only to implement design patterns in your application, but also to learn how the development team applies patterns when writing code. Enterprise Library embodies many design patterns, and demonstrates good architectural and coding techniques. The source code for the entire library is provided, so you can explore the implementations and reuse the techniques in your own applications.

And, finally, it is free! Or rather, it is distributed under the Microsoft Public License (MSPL) that grants you a royalty-free license to build derivative works, and distribute them free—or even sell them. You must retain the attribution headers in the source files, but you can modify the code and include your own custom extensions. Do you really need any other reasons to try Enterprise Library?

Note

You'll notice that, even though we didn't print "Don't Panic!" in large friendly letters on the cover, this book does take a little time to settle down into a more typical style of documentation, and start providing practical examples. However, you can be sure that—from here on in—you'll find a whole range of guidance and examples that will help you master Enterprise Library quickly and easily. There are other resources to help if you're getting started with Enterprise Library (such as hands-on-labs), and there's help for existing users as well (such as the breaking changes and migration information for previous versions) available at https://www.codeplex.com/entlib/. You can also visit the source code section of the site to see what the Enterprise Library team is working on as you read this guide.

Some Fundamentals of Enterprise Library

Before we dive into our tour of the application blocks and features of Enterprise Library, you need to grasp some fundamentals. In this chapter, the Librarian will help you explore topics such as how to install and deploy the library, and how to perform initial configuration. After that, you'll be free to skip to any of the other chapters and learn more about the ways that each block helps you to simplify your code and manage your crosscutting concerns. For more information about the topics covered in this chapter, see the product documentation installed with Enterprise Library, or the online documentation available at https://go.microsoft.com/fwlink/?LinkId=188874.

Choosing Which Blocks to Install

Enterprise Library is a "pick and mix" candy store, where you choose just the features you want to use and simply disregard the rest. Of course, before you can choose your favorite candies from the tempting displays in the candy store, you need to find a paper bag to hold them. You can think of this as a prerequisite for picking and mixing, and a basic feature that you will use every time—irrespective of whether you choose gummy bears, chocolate-covered hazelnuts, or mint imperials.

Likewise, with Enterprise Library, there are prerequisites and basic features. The main prerequisite before you start development is to install the binaries and support files onto your machine. The basic features that you need every time you use Enterprise Library are the core assemblies that implement access to configuration, object creation, and ancillary features used by all of the blocks.

However, when you install Enterprise Library, you can choose which of the application blocks you want to install; though it is generally a good idea to install them all unless you are sure you will not use specific blocks. Some blocks have dependencies on other blocks, and installing all of them while developing your applications will simplify configuration and ensure that you do not have to re-run the installer to add other blocks later on. When you come to deploy your application, you only need to deploy the blocks you are using and their dependent blocks.

For example, the Exception Handling block depends on the Logging block for logging exception information. Table 1 shows the full list of these dependencies.

Table 1

Application block optional dependencies

Application Block

Optional dependencies

Caching Block

May use the Data Access block to cache data in a database.

May use the Cryptography block to encrypt cached data.

Exception Handling Block

May use the Logging block to log exception information.

May use the Data Access block to log exception information to a database.

Logging Block

May use the Data Access block to log to a database.

Security Block

May use the Caching block to cache credentials.

May use the Data Access block to cache credentials in a database.

May use the Cryptography block to encrypt cached credentials.

The configuration tools will automatically add the required block to your application configuration file with the default configuration when required. For example, when you add a Logging handler to an Exception Handling block policy, the configuration tool will add the Logging block to the configuration with the default settings.

The seven application blocks we cover in this guide are the functional blocks that are specifically designed to help you manage a range of crosscutting concerns. All of these blocks depend on the core features of Enterprise Library, which in turn depend on the Unity dependency injection and interception mechanism (the Unity Application Block) to perform object creation and additional basic functions.

Installing Enterprise Library

To begin using Enterprise Library you must first install it. You can download the current version from https://msdn.microsoft.com/entlib/. Simply run the Microsoft Installer (MSI) package to begin the installation, and select the blocks and features you want to install. This installs the precompiled binaries ready for you to use, along with the accompanying tools and resources such as the configuration editor and scripts to install the samples and instrumentation.

If you want to examine the source code, and perhaps even modify it to suit your own requirements, be sure to select the option to install the source code when you run the installer. The source code is included within the main installer as a separate package, which allows you to make as many working copies of the source as you want and go back to the original version easily if required. If you choose to install the source, then it's also a good idea to select the option to have the installer compile the library for you so that you are ready to start using it straight away. However, if you are happy to use the precompiled assemblies, you do not need to install or compile the source code.

After the installation is complete, you will see a Start menu entry containing links to the Enterprise Library tools, source code installer, and documentation. The tools include batch files that install instrumentation, database files, and other features. There are also batch files that you can use to compile the entire library source code, and to copy all the assemblies to the bin folder within the source code folders, if you want to rebuild the library from the source code.

Assemblies and References

It's not uncommon, when people first look at Enterprise Library, to see a look of mild alarm spread across their faces. Yes, there are quite a few assemblies, but remember:

  • You only need to use those directly connected with your own scenario.
  • Several are required for only very special situations.
  • The runtime assemblies you will use in your applications are mostly less than 100 KB in size; and the largest of all is only around 500 KB.
  • In most applications, the total size of all the assemblies you will use will be between 1 and 2 MB.

The assemblies you should add to any application that uses Enterprise Library are the common (core) assembly, the Unity dependency injection mechanism (if you are using the default Unity container), and the container service location assembly:

  • Microsoft.Practices.EnterpriseLibrary.Common.dll
  • Microsoft.Practices.Unity.dll
  • Microsoft.Practices.Unity.Interception.dll
  • Microsoft.Practices.ServiceLocation.dll

Note

You will also need the assembly Microsoft.Practices.Unity.Configuration.dll if you wish to reference specific Unity configuration classes in your code. However, in the majority of cases, you will not require this assembly.

In addition to the required assemblies, you must reference the assemblies that implement the Enterprise Library features you will use in your application. There are several assemblies for each application block. Generally, these comprise a main assembly that has the same name as the block (such as Microsoft.Practices.EnterpriseLibrary.Logging.dll), plus additional assemblies that implement specific handlers or capabilities for the block. You only need these additional assemblies if you want to use the features they add. For example, in the case of the Logging block, there is a separate assembly for logging to a database (Microsoft.Practices.EnterpriseLibrary.Logging.Database.dll). If you do not log to a database, you do not need to reference this additional assembly.

GAC or Bin, Signed or Unsigned?

All of the assemblies are provided as precompiled signed versions that you can install into the global assembly cache (GAC) if you wish. However, if you need to run different versions of Enterprise Library assemblies side by side, this may be problematic and you may prefer to locate them in folders close to your application.

You can then reference the compiled assemblies in your projects, which automatically copies them to the bin folder. In a Web application, you can simply copy them directly to your application's bin folder. This approach gives you simple portability and easy installation.

Alternatively, you can install the source code for Enterprise Library and use the scripts provided to compile unsigned versions of the assemblies. This is useful if you decide to modify the source code to suit your own specific requirements. You can strong name and sign the assemblies using your own credentials afterwards if required.

For more information about side-by-side operation and other deployment issues, see the documentation installed with Enterprise Library and available online at https://go.microsoft.com/fwlink/?LinkId=188874.

Importing Namespaces

After you reference the appropriate assemblies in your projects, you will probably want to add using statements to your project files to simplify your code and avoid specifying objects using the full namespace names. Start by importing the two core namespaces that you will require in every project that uses Enterprise Library:

  • Microsoft.Practices.EnterpriseLibrary.Common
  • Microsoft.Practices.EnterpriseLibrary.Common.Configuration

Note

Depending on how you decide to work with Enterprise Library in terms of instantiating the objects it contains, you may need to import two more namespaces. We'll come to this when we look at object instantiation in Enterprise Library a little later in this chapter.

You will also need to import the namespaces for the specific application blocks you are using. Most of the Enterprise Library assemblies contain several namespaces to organize the contents. For example, as you can see in Figure 3, the main assembly for the Logging block (one of the more complex blocks) contains a dozen subsidiary namespaces. If you use classes from these namespaces, such as specific filters, listeners, or formatters, you may need to import several of these namespaces.

Figure 3

Namespaces in the Logging block

Ff953191.0122caa7-f0ae-4f14-81a8-e5ad7fbc7b54(en-us,PandP.50).png

Configuring Enterprise Library

Before the original individual application blocks were combined into Enterprise Library, one of the biggest challenges for users was configuration. You had to edit the sections of the application configuration file manually, which proved to be error-prone and just plain annoying. In Enterprise Library, you have a choice of tools for performing configuration and a wealth of opportunities for defining and managing your configuration information.

This flexibility comes about because Enterprise Library uses configuration sources to expose configuration information to the application blocks and the core features of the library. The configuration sources can read configuration from standard .NET configuration files (such as App.config and Web.config), from other files, from a database (using the example SQL Configuration Source available from https://entlib.codeplex.com), and can also take into account Group Policy rules for a machine or a domain.

In addition, you can use the fluent interface or the .NET configuration API to create and populate configuration sources programmatically, merge parts of your configuration with a central shared configuration, generate merged configuration files, and generate different configurations for individual run-time environments. For more information about these more advanced configuration scenarios, see Appendix D, "Enterprise Library Configuration Scenarios."

The Configuration Tools

Enterprise Library includes a stand-alone configuration console, and a configuration editor that integrates with Microsoft Visual Studio®. The stand-alone console is provided as versions specifically aimed at the 32-bit (x86) platform and versions compiled for any platform. For each of these platforms, there is a separate version of the console for the 3.5 and 4.0 versions of the .NET Framework. You can even copy it (and the assemblies it uses) to a machine that does not have Enterprise Library installed if you just want to perform post-deployment configuration and system administration. Figure 4 shows the configuration console with some of the application blocks covered in this book installed into the configuration.

Figure 4

The Enterprise Library configuration console

Ff953191.65725249-6770-4973-b8ea-5adfce09a30c-thumb(en-us,PandP.50).png

The Visual Studio configuration editor displays an interface very similar to that shown in Figure 4, but allows you to edit your configuration files with a simple right-click in Solution Explorer.

Using the Configuration Tools

The most common scenario for basic configuration of an application is to store the configuration information in a local configuration file (such as Web.config or App.config). You can create a new Enterprise Library configuration in the configuration console and then save it to disk, or you can open an existing configuration file and edit it to add Enterprise Library to your application.

Even if you use the more advanced approaches described in Appendix D, "Enterprise Library Configuration Scenarios," the techniques for defining your Enterprise Library configuration are basically the same. The general procedure for configuring an application is as follows:

  1. Open the stand-alone configuration tool from your Start menu, or right-click on a configuration file in Visual Studio Solution Explorer and click Edit Enterprise Library V5 Configuration.

  2. Click the Blocks menu and select the block you want to add to the configuration. This adds the block with the default settings.

    • If you want to use the configuration console to edit values in the <appSettings> section of your configuration file, select Add Application Settings.
    • If you want to enable instrumentation for Enterprise Library, select Add Instrumentation Settings.
    • If you want to use an alternative source for your configuration, such as a custom XML file, select Add Configuration Settings.
  3. To view the configuration settings for each section, block, or provider, click the right-facing arrow next to the name of that section, block, or provider. Click it again, or press the Spacebar key, to collapse this section.

  4. To view the properties pane for each main configuration section, click the downward-facing double arrow. Click it again to close the properties pane.

  5. To add a provider to a block, depending on the block or the type of provider, you either right-click the section in the left column and select the appropriate Add item on the shortcut menu, or click the plus-sign icon in the appropriate column of the configuration tool. For example, to add a new exception type to a policy in the Exception Handling block, right-click the Policy item and click Add Exception Type.

    Note

    When you rename items, the heading of that item changes to match the name. For example, if you renamed the default Policy item in the Exception Handling block, the item will show the new name instead of "Policy."

  6. Edit the properties of the section, block, or provider using the controls in that section for that block. You will see information about the settings required, and what they do, in the subsequent chapters of this guide. For full details of all of the settings that you can specify, see the documentation installed with Enterprise Library for that block.

  7. To delete a section or provider, right-click the section or provider and click Delete on the shortcut menu. To change the order of providers when more than one is configured for a block, right-click the section or provider and click the Move Up or Move Down command on the shortcut menu.

  8. To set the default provider for a block, such as the default Database for the Data Access block, click the down-pointing double arrow icon next to the block name and select the default provider name from the drop-down list. In this section you can also specify the type of provider used to encrypt this section, and whether the block should demand full permissions.

    Note

    For more details about encrypting configuration, see the next section of this chapter. For information about running the block in partial trust environments, which requires you to turn off the Require Permission setting, see the documentation installed with Enterprise Library.

  9. To use a wizard to simplify configuration for a common task, such as configuring logging to a database, open the Wizards menu and select the one you require. The wizard will display a series of dialogs that guide you through setting the required configuration.

  10. If you want to configure different settings for an application based on different deployment scenarios or environments, open the Environments menu and click New Environment. This adds a drop-down list, Overrides on Environment, to each section. If you select Override Properties in this list, you can specify the settings for each new environment that you add to the configuration. This feature is useful if you have multiple environments that share the same basic configuration but require different property settings. It allows you to create a base configuration file (.config) and an environment delta file that contains the differences (.dconfig). See Appendix D, "Enterprise Library Configuration Scenarios" for information on configuring and using multiple environments.

  11. As you edit the configuration, the lower section of the tool displays any warnings or errors in your configuration. You must resolve all errors before you can save the configuration.

  12. When you have finished configuring your application, use the commands on the File menu to save it as a file in your application folder with the appropriate name; for example, use Web.config for a Web application and App.config for a Windows Forms application.

You can, of course, edit the configuration files using a text or XML editor, but this is likely to be a more tedious process compared to using the configuration console. However, it may be a useful approach for minor changes to the configuration when the application is running on a server where the configuration console is not installed. Enterprise Library also contains an XML configuration schema that you can use to enable IntelliSense® and simplify hand editing of the configuration files.

Note

To enable the Enterprise Library XML schema in Visual Studio, open the configuration file, open the XML menu, and click Schemas. In the XML Schemas dialog, locate the Enterprise Library schema and change the value in the Use column to Use this schema. Then click OK.

Encrypting Configuration Sections

Probably the most common approach for storing configuration information for your applications that use Enterprise Library is to use an App.config or Web.config file stored in the root folder of your application. That's fine, but you may be concerned that anyone who happens to stroll past the server (either physically, or virtually over the Internet) will be able to open the file and see sensitive details. These might include connection strings for the Data Access block, validation rules for the Validation block, or connection information used by the Logging block to communicate with Windows Message Queuing.

While in theory, you will protect your configuration files by physically securing the server and not leaving it running under a logged-on administrator account, you can (and probably should) add an extra layer of protection by encrypting sections of your configuration files. The configuration tools can do this for you automatically; all you need to do is set the ProtectionProvider property of the specific block or configuration section that you want to encrypt. For more information, see Appendix E, "Encrypting Configuration Files."

Instantiating and Using Enterprise Library Objects

After you have referenced the assemblies you need, imported the required namespaces, and configured your application, you can start to think about creating instances of the Enterprise Library objects you want to use in your applications. As you will see in each of the following chapters, the Enterprise Library application blocks are optimized for use as loosely coupled components in almost any type of application. In addition, the change in this release to using a dependency injection container to generate instances of Enterprise Library objects means that you can realize the benefits of contemporary design patterns and solution architectures more easily.

Note

By default, Enterprise Library uses the Unity dependency injection mechanism, which is provided as part of Enterprise Library. However, it’s possible to configure Enterprise Library to use any dependency injection container—or other underlying mechanism—that exposes the required configuration information though an implementation of the IServiceLocator interface. See Appendix B, "Dependency Injection in Enterprise Library," and https://commonservicelocator.codeplex.comfor more information.

In Appendix A, "Dependency Injection with Unity," we take a more in-depth look at what a dependency injection container actually is, and how it can assist you in applying design patterns that follow the dependency inversion principle (DIP); in particular, how the Dependency Injection (DI) pattern can help you to create more decoupled applications that are easier to build, test, and maintain. However, you don't need to understand this or learn about DI to be able to use Enterprise Library. You can create instances of Enterprise Library objects easily and quickly with a single line of code.

Enterprise Library Objects, Facades, and Factories

Each of the application blocks in Enterprise Library contains one or more core objects that you typically use to access the functionality of that block. An example is the Exception Handling Application Block, which provides a facade named ExceptionManager that exposes the methods you use to pass exceptions to the block for handling. The following table lists the commonly used objects for each block.

Application Block

Non-static Instance or Factory

Caching

ICacheManager

Cryptography

CryptographyManager

Data Access

Database

Exception Handling

ExceptionManager

Logging

LogWriter

TraceManager

Security

ISecurityCacheProvider

IAuthorizationProvider

Validation

ValidatorFactory

ConfigurationValidatorFactory

AttributeValidatorFactory

ValidationAttributeValidatorFactory

Note

There are also task-specific objects in some blocks that you can create directly in your code in the traditional way using the new operator. For example, you can create individual validators from the Validation Application Block, or log entries from the Logging Application Block. We show how to do this in the examples for each application block chapter.

To use the features of an application block, all you need to do is create an instance of the appropriate object, facade, or factory listed in the table above and then call its methods. The behavior of the block is controlled by the configuration you specified, and often you can carry out tasks such as exception handling, logging, caching, and encrypting values with just a single line of code. Even tasks such as accessing data or validating instances of your custom types require only a few lines of simple code. So, let's look at how you create instances of the Enterprise Library objects you want to use.

Creating Instances of Enterprise Library Types

In this release of Enterprise Library, there are two recommended approaches to creating instances of the Enterprise Library objects. The decision as to which you use is based solely on the way you decide to architect your application. You can use the simple approach of obtaining instances using the Enterprise Library service locator, which provides access to the Unity container that holds the Enterprise Library configuration information. Alternatively, if you are already a DI convert, you can take charge of the entire process by creating and populating a container and using it to create and manage both Enterprise Library objects and your own custom types. We'll look at both approaches next.

The Simple Approach — Using the Enterprise Library Service Locator

When you initially create an instance of an Enterprise Library type in your application code, the underlying mechanism reads your configuration information into a container and exposes it to your code through a service locator that is initialized as part of the Enterprise Library configuration mechanism. This service locator provides methods that you can call at any point in your application code to obtain configured instances of any Enterprise Library type.

For example, if you are using the Logging Application Block, you can obtain a reference to a LogWriter using a single line of code, and then call its Write method to write your log entry to the configured targets, as shown here.

var writer = EnterpriseLibraryContainer.Current.GetInstance<LogWriter>();
writer.Write("I'm a log entry created by the Logging block!");

Note

Notice that this code uses type inference through the var keyword. The variable will assume the type returned by the assignment; this technique can make your code more maintainable.

If you configured more than one instance of a type for a block, such as more than one Database for the Data Access Application Block, you can specify the name when you call the GetInstance method. For example, you may configure an Enterprise Library Database instance named Customers that specifies a Microsoft SQL Server® database, and a separate Database instance named Products that specifies another type of database. In this case, you specify the name of the object you want to resolve when you call the GetInstance method, as shown here.

var customerDb 
   = EnterpriseLibraryContainer.Current.GetInstance<Database>("Customers");

You don't have to initialize the block, read configuration information, or do anything other than call the methods of the service locator. For many application scenarios, this simple approach is ideal for obtaining instances of the Enterprise Library types you want to use.

The Sophisticated Approach — Accessing the Container Directly

If you want to take advantage of design patterns such as Dependency Injection and Inversion of Control in your application, you will probably already be considering the use of a dependency injection mechanism to decouple your components and layers, and to resolve types. If this is the case, the more sophisticated approach to incorporating Enterprise Library into your applications will fit well with your solution architecture.

Instead of allowing Enterprise Library to create, populate, and expose a default container that holds just Enterprise Library configuration information, you can create the container and populate it yourself—and hold onto a reference to the container for use in your application code. This not only allows you to obtain instances of Enterprise Library objects, it also lets you use the container to implement dependency injection for your own custom types. Effectively, the container itself becomes your service locator.

For example, you can create registrations and mappings in the container that specify features such as the dependencies between the components of your application, mappings between types, the values of parameters and properties, interception for methods, and deferred object creation.

You may be thinking that all of these wondrous capabilities will require a great deal of code and effort to achieve; however, they don't. To initialize and populate the default Unity container with the Enterprise Library configuration information and make it available to your application, only a single line of code is required. It is shown here:

var theContainer = new UnityContainer()
                       .AddNewExtension<EnterpriseLibraryCoreExtension>();

Now that you have a reference to the container, you can obtain an instance of any Enterprise Library type by calling the container methods directly. For example, if you are using the Logging Application Block, you can obtain a reference to a LogWriter using a single line of code, and then call its Write method to write your log entry to the configured targets.

var writer = theContainer.Resolve<LogWriter>();
writer.Write("I'm a log entry created by the Logging block!");

And if you configured more than one instance of a type for a block, such as more than one database for the Data Access Application Block, you can specify the name when you call the Resolve method, as shown here:

var customerDb = theContainer.Resolve<Database>("Customers");

Note

You may have noticed the similarity in syntax between the Resolve method and the GetInstance method we used earlier. Effectively, when you are using the default Unity container, the GetInstance method of the service locator simply calls the Resolve method of the Unity container. It therefore makes sense that the syntax and parameters are similar. Both the container and the service locator expose other methods that allow you to get collections of objects, and there are both generic and non-generic overloads that allow you to use the methods in languages that do not support generics.

One point to note if you choose this more sophisticated approach to using Enterprise Library in your applications is that you should import two additional namespaces into your code. These namespaces include the container and core extension definitions:

  • Microsoft.Practices.EnterpriseLibrary.Common.Configuration.Unity
  • Microsoft.Practices.Unity

Pros and Cons of Object Instantiation

If you haven't already decided which approach to follow for creating Enterprise Library objects, the following table will help you to understand the advantages and disadvantages of each one.

Object instantiation technique

Advantages

Considerations

Using the Enterprise Library service locator

Requires no initialization code. The service locator is made available automatically.

You can resolve types anywhere in your application code. You don't need to hold onto a reference to the container.

You can only resolve Enterprise Library types (as interfaces, abstract types, or concrete types that are registered automatically).

You cannot manipulate, or add registrations or mappings to the container.

Using the container as the service locator

You can directly access all the functionality of the Unity container.

You can iterate over the contents and read or manipulate the registrations and mappings (though you should not attempt to change the Enterprise Library configuration information).

You can add and remove your own registrations and mappings, allowing you to take full advantage of DI techniques.

Requires initialization, though this is simply one line of code executed at application startup, or simple configuration settings, when you use the default Unity container.

Request-based applications such as ASP.NET and Web services require additional code to store the container reference and resolve the dependencies of the request class (such as the Page).

One of the prime advantages of the more sophisticated approach of accessing the container directly is that you can use it to resolve dependencies of your own custom types. For example, assume you have a class named TaxCalculator that needs to perform logging and implement a consistent policy for handling exceptions that you apply across your entire application. Your class will contain a constructor that accepts an instance of an ExceptionManager and a LogWriter as dependencies.

public class TaxCalculator 
{
  private ExceptionManager _exceptionManager;
  private LogWriter _logWriter;

  public TaxCalculator(ExceptionManager em, LogWriter lw) 
  {
    this._exceptionManager = em;
    this._logWriter = lw;
  }
  ...
}

If you use the Enterprise Library service locator approach, you could simply obtain these instances within the class constructor or methods when required, rather than passing them in as parameters. However, a more commonly used approach is to generate and reuse the instances in your main application code, and pass them to the TaxCalculator when you create an instance.

var exManager 
  = EnterpriseLibraryContainer.Current.GetInstance<ExceptionManager>();
var writer 
  = EnterpriseLibraryContainer.Current.GetInstance<LogWriter>();
TaxCalculator calc = new TaxCalculator(exManager, writer);

Alternatively, if you have created and held a reference to the container, you just need to resolve the TaxCalculator type through the container. Unity will instantiate the type, examine the constructor parameters, and automatically inject instances of the ExceptionManager and a LogWriter into them. It returns your new TaxCalculator instance with all of the dependencies populated.

TaxCalculator calc = theContainer.Resolve<TaxCalculator>();

More Reasons to be Sophisticated

It is clear from the preceding examples that managing the container yourself offers considerable advantages in all but the simplest applications or scenarios. And the example you've seen for using dependency injection only scratches the surface of what you can do using the more sophisticated approach. For example, if you have a reference to the container, you can:

  • Manage the lifetime of your custom types. They can be resolved by the container as singletons, with a lifetime based on the lifetime of the object that created them, or as a new instance per execution thread.
  • Implement patterns such as plug-in and service locator by mapping interfaces and abstract types to concrete implementations of your custom types.
  • Defer creation of the resolved custom type until it is actually required.
  • Specify dependencies and values for parameters and properties of the resolved instances of your custom types.
  • Apply interception to your custom types to modify their behavior, implement management of crosscutting concerns, or add additional functionality.
  • Set up hierarchies of dependencies that are automatically populated to achieve maximum decoupling between components, assist in debugging, simplify testing, and reduce maintenance cost and effort.

When you use the default Unity container, you have a powerful general-purpose dependency injection mechanism in your arsenal. You can define and modify registrations and mappings in the container programmatically at run time, or you can define them using configuration files. Appendix A, "Dependency Injection with Unity," contains more information about using Unity.

To give you a sense of how easy it is to use, the following code registers a mapping between an interface named IMyService and a concrete type named CustomerService, specifying that it should be a singleton.

theContainer.RegisterType<IMyService, CustomerService>(
                          new ContainerControlledLifetimeManager());

Then you can resolve the single instance of the concrete type using the following code.

IMyService myServiceInstance = theContainer.Resolve<IMyService>();

This returns an instance of the CustomerService type, though you can change the actual type returned at run time by changing the mapping in the container. Alternatively, you can create multiple registrations or mappings for an interface or base class with different names and specify the name when you resolve the type.

Unity can also read its configuration from your application's App.config or Web.config file (or any other configuration file). This means that you can use the sophisticated approach to creating Enterprise Library objects and your own custom types, while being able to change the behavior of your application just by editing the configuration file.

Note

If you want to load type registrations and mappings into a Unity container from a configuration file, you must add the assembly Microsoft.Practices.Unity.Configuration.dll to your project, and optionally import the namespace Microsoft.Practices.Unity.Configuration into your code. This assembly and namespace contains the extension to the Unity container for loading configuration information.

For example, the following extract from a configuration file initializes the container and adds the same custom mapping to it as the RegisterType example shown above.

<unity>
  <alias alias="CoreExtension"
         type="Microsoft.Practices.EnterpriseLibrary.Common.Configuration
              .Unity.EnterpriseLibraryCoreExtension,
               Microsoft.Practices.EnterpriseLibrary.Common" />  
  <namespace name="Your.Custom.Types.Namespace" />
  <assembly name="Your.Custom.Types.Assembly.Name" />  
  <container>
    <extension type="CoreExtension" />
    <register type="IMyService" mapTo="CustomerService">
      <lifetime type="singleton" /> 
    </register>
  </container>
</unity>

Then, all you need to do is load this configuration into a new Unity container. This requires just one line of code, as shown here.

var theContainer = new UnityContainer().LoadConfiguration();

Note

Other techniques we demonstrate in Appendix A, "Dependency Injection with Unity," include using attributes to register type mappings and dependencies, defining named registrations, and specifying dependencies and values for parameters and properties.

The one point to be aware of when you use the more sophisticated technique for creating objects is that your application is responsible for managing the container, holding a reference to it, and making that reference available to code that must access the container. In forms-based applications that automatically maintain global state (for example, applications built using technologies such as Windows Forms, Windows Presentation Foundation (WPF), and Silverlight®), you can use an application-wide variable for this.

However, in request-based applications built using technologies such as ASP.NET, ASMX, and Windows Communication Foundation (WCF), you generally require additional code to maintain the container and make it available for each request. We discuss some of the ways that you can achieve this in Appendix B, "Dependency Injection in Enterprise Library," and you will find full details in the documentation installed with Enterprise Library and available online at https://go.microsoft.com/fwlink/?LinkId=188874.

Getting Objects from Previous Versions of Enterprise Library

If you have used versions of Enterprise Library prior to version 5.0, you may be more familiar with the previous approach to creating objects within your application code. Earlier versions generally supported or recommended the use of a series of static facades. While these facades are still supported in version 5.0 for backward compatibility with existing applications, they are no longer the recommended approach and may be deprecated in future releases.

Figure 5 summarizes all the approaches you can use to get access to the features of Enterprise Library. 1 and 2 are the recommended approaches for Enterprise Library 5.0; 3 and 4 are still supported to make it easier to upgrade your existing applications that use a previous version of Enterprise Library.

Ff953191.441da4ba-fa2e-4da3-bac1-89897580d80b-thumb(en-us,PandP.50).png

Next | Previous | Home | Community