Resource Descriptor

Summary

Isolates platform- and data-source-dependent behavior within a single
component. A resource descriptor exposes platform and datasource specifics as
generic logical operations. This allows the majority of data access code to
remain independent of its physical environment.

Database access code almost always has to address ADO.NET provider-dependent
issues on a regular basis. For example, while SQL statements are mostly portable
among database platforms, many products extend SQL syntax to provide additional
features and optimizations that the standard does not support. As a result, data
access code that must support different database platforms often need the
ability to choose among different SQL statements that represent the same logical
data access operation. As another example, many databases implement their
own native errors and codes and applications deal with these errors as ADO.NET
provide-generated exceptions. As a result, those applications that
filter/depend on certain error codes/messages to make decisions need to handle
different set of error codes for each database platform.

A naive/quick-fix-do-it-later solution would be to pepper data access code
with checks for each supported database platform. A Resource Descriptor
describes a way to customize environment-dependent features and operations
within an isolated environment. How a resource descriptor accomplishes this
is presented in the next section.

The following diagram illustrates the static structure of the Resource
Descriptor:

The IResourceDescriptor interface defines operations for any application code
that changes depending on the runtime- or database-environment. These operations
can retrieve attributes or implement functional behavior. ConcreteResourceDescriptor
implements IResourceDescriptor in terms of a
specific environment or database product. You write client code in terms of the IResourceDescriptor
interface but interact directly with a ConcreteResourceDescriptor
instance.

Your data access code will have to interact with a particular IResourceDescriptor
implementation at runtime. It is best to provide an environment-neutral mechanism
that resolves which particular IResourceDescriptor
implementation to use based on some contextual information such as database
connection or data source representation. Here are some alternatives:

Static resolution moduleYou can define a static operation that can be used to resolve
appropriate IResourceDescriptor interfaces. For
example:

Method Parameters
Just as you would pass a database connection string to methods that require
it, you can also pass a ResourceDescriptor
implementation as an extra parameter to relevant operations. While this is a
clean approach that avoids the requirement for any static or global
resolution code, it does contribute to parameter obfuscation, especially for
operations that define many parameters. For example:

Configuration Files / Database tables
You can define configuration-file or database-table entries for each
supported environment. Obviously there needs to be supporting methods to
read and resolve entries in a generic manner. This approach is as it allows
the addition of new environments or database platforms without having to
change any code.

Isolates environment or database dependencies without hardwirring
Resource descriptors encapsulate environment dependencies within their own
modules leaving application code and generic database access layers free of
hardwired conditional operations. Obviously this leads to maintainable code
and makes it easy to add support for new database products without modifying
application or data access code.

Provides tailored database metadata
You can define any metadata you want inside a resource descriptor, making
them more straightforward to use within an application.