Export (0) Print
Expand All
Debugger Visualizations, Garbage Collection
Use the Visual Studio 2005 Bootstrapper to Kick-Start Your Installation
CLR Inside Out: Improving Application Startup Time
Unit Testing Tips: Write Maintainable Unit Tests That Will Save You Time And Tears
Program Customized Testing Environments Without Trashing Your Machine
Bridge the Gap Between Development and Operations with Whitehorse
Visual Studio 2005: Create Reusable Project And Item Templates For Your Development Team
Expand Minimize

Introduction to Visual Studio Data Designer Extensibility (DDEX)

Visual Studio 2005
 

Milind Lele, Stephen Provine, Nick Gattuccio
Microsoft Corporation

May 2005

Summary: This whitepaper provides an introduction to Visual Studio Data Designer Extensibility (DDEX). This is the mechanism through which a .NET data provider can integrate with Visual Studio 2005. (20 printed pages)

Contents

Introduction
   Prerequisites
Data Designer Extensibility
   What is DDEX?
   Architectural overview
Implementing a DDEX Provider
   Registering a provider
   Integration with the connection dialog
   Integrating with the Server Explorer
   Integrating with project data design time
   Integrating with the Properties window from Server Explorer
   Adding menu items in Server Explorer
Conclusion

Introduction

Data Designer Extensibility (DDEX) extends the Visual Studio design-time experience by allowing Visual Studio components (data designers and the Server Explorer, for example) to interact with third-party data sources and their object hierarchies. Put simply, DDEX is a technology that allows data providers to integrate with Visual Studio.

Different data sources pose different requirements. Supporting design-time operations against a SQL Server database, for example, is much different than supporting operations against a SAP or Oracle database, or against Microsoft Excel, or against a .cvs or .xml file. Each data source has different characteristics, both syntactical and structural, and hence requires a datasource-specific, .NET data provider.

This whitepaper provides an introduction to DDEX. In addition to providing a conceptual overview, it describes how different architectural components of DDEX work together, and shows how to accomplish common tasks when you implement a DDEX provider. However, this is not an exhaustive discussion of all the capabilities and features of DDEX. For a deeper understanding, please refer to the help documentation provided with the DDEX SDK.

After reading this document you should be able to:

  • Understand the key concepts and architectural framework of DDEX.
  • Obtain the DDEX SDK and begin implementing a simple DDEX provider that does some or all of the following:
    • Extend the Visual Studios Data Connection dialog box.
    • Displays your data source objects in the Visual Studio Server Explorer.
    • Achieve basic integration with project designers.

The document is organized into two main parts:

The first provides an overview of DDEX. This section gives a high-level description of different parts of DDEX and how they relate to one another. It also describes how a DDEX provider plays into the overall architecture.

The second section—Implementing a DDEX Provider—is a collection of procedures that you, as a DDEX provider writer, need to follow to effect a DDEX implementation. This section describes how to do the following:

  • Register a DDEX provider
  • Integrate with the Connection dialog box
  • Integrate with Server Explorer
  • Expose object data that Visual Studio designers can use
  • Expose custom properties on data objects displayed in Server Explorer
  • Add custom menus to Server Explorer nodes

Prerequisites

To implement a DDEX provider based on descriptions in this whitepaper you would need the following:

  • An ADO.NET data provider
  • Visual Studio 2005
  • The Data Designer Extensibility (DDEX) SDK (part of the Visual Studio Industry Partner [VSIP] installation)

Data Designer Extensibility

What is DDEX?

DDEX defines a systematic approach to extending data designers and data-related features of Visual Studio 2005 to object hierarchies provided by third-party data sources. This provides design-time access to external data source objects and entitles these objects to "full citizenship" in the Visual Studio design-time experience.

DDEX provides this high-level extensibility by providing a library of managed classes (the DDEX managed class library) that allows you to implement a Visual Studio extension to a .NET data provider. Such an extension implementation is called a DDEX provider.

You can use a DDEX provider implementation to extend the following Visual Studio features:

Data Connection and login dialog boxes
These dialog boxes are frequently specific to a data source. DDEX allows a provider to expose its own control that can be hosted in the Visual Studio Data Connection dialog box, and be used when connecting to that data source.
Database Explorer
You can enumerate objects from the data source for display as hierarchy nodes in the Visual Studio Server Explorer; you can control which objects to enumerate and how they should be presented in the UI.
Drag-and-drop
You can drag-and-drop data object nodes from the Server Explorer onto data designers.
Custom designers
A DDEX provider can supply custom designers, in place of default Visual Studio designers, to view and edit data objects. For instance, you may use your own table designer to view and/or edit tables from your data source.

In addition to making the integration easy, DDEX also provides a common model for all data providers—simplifying both the implementation as well as the common user experience.

Architectural overview

Viewed structurally, there are four parts to a DDEX implementation:

  • Data source
  • Data provider
  • DDEX
  • Visual Studio

DDEX itself is composed of two key parts:

DDEX Support Entities
Support entities are the set of managed classes that a DDEX provider must implement; this implementation is, by definition, the DDEX provider.
DDEX Services
The DDEX Services are a set of managed classes that Visual Studio calls to access functionality offered by a DDEX provider.

ms379576.ddexintro01(en-US,VS.80).gif

Figure 1. DDEX architecture

Support Entities

Support entities are quite simply a set of managed classes that are implemented to provide specific Visual Studio extensions. In fact, it is the implementation of these support entity classes that constitutes a DDEX provider.

Broadly speaking, support entities fall into three categories:

  • Connection support
  • Data object support
  • Data view support

Connection support

Connection support allows a DDEX provider to expose means to specify connection information. This may be done either programmatically, if the connection string is known, or by prompting the user for connection information. In either case the provider needs to expose properties that are relevant to that data provider.

Optionally, a DDEX provider can implement and expose a connection control to be hosted in the Visual Studio Data Connection dialog box. This control is used when its associated data provider and/or data source is selected by the user.

ms379576.ddexintro02(en-US,VS.80).gif

Figure 2. Visual Studio Data Connection dialog box

The figure above shows the Data Connection dialog box. The portion of the dialog indicated by a red block is the control that is supplied by the Microsoft SQL Server data provider. For your data sources you can substitute this with your own connection control.

Data object support

Data object support allows a DDEX provider to define the objects that are exposed by the data provider for a given data source. This is defined in an XML file.

The DDEX metadata engine includes a set of strongly typed classes that allow it to recognize generic objects like tables, views, and stored procedures, as well as generic properties on these objects. These are known as concepts. Data object support allows you to map Types that are supported by your data provider to one of these generic Visual Studio concepts, which are recognizable by the Visual Studio metadata engine.

In addition to defining the data source object types, data object support must also specify the following:

  • How to enumerate objects of the specified type. This is done by specifying a handler that is called when enumeration is required.
  • How to retrieve a type's object metadata, such as the identifier or properties.
  • How to construct a clipboard object if a copy operation is performed on an object of the specified type. DSRef is the clipboard format used by data designers in Visual Studio. Objects that support DSRef can be dragged and dropped onto Visual Studio data designers.

Data view support

Data view support defines the shape of the hierarchy as it is displayed in the Visual Studio Server Explorer. You can define multiple views for the same data source. For instance, in Visual Studio we provide three views for SQL Server.

In addition to defining the hierarchy you can specify, for each node, things like localized display names, context menu commands (command name, clsid, and handler), icons, as well as other node attributes.

DDEX Services

DDEX provides a set of Visual Studio services that expose functionality that has been provided by the support entities. Some of the more important Visual Studio services include the following:

Provider Manager
Allows you to enumerate registered DDEX providers and query for objects of a specific type.
Data Provider Object Factory
Provides a way to create data objects implemented by the DDEX provider.
Data Connection Factory
Allows you to create connection objects.
Data Connection Dialog
Allows you to prompt the user for connection information and create connection objects.

Implementing a DDEX Provider

In this section we detail several procedures you must undertake to implement a DDEX provider.

Registering a provider

The first and most important step in creating a DDEX provider is to register it. Registration of a provider enables visibility inside Visual Studio, as well as specifying the capabilities of the provider.

There are two main ways that a DDEX provider can be registered. The first and simplest approach covers a common case: extension of an ADO.NET 2.0-compliant data provider for Visual Studio integration. The second, more advanced approach extends on the standard Visual Studio extensibility package (VSPackage) model. In this paper we will discuss the first. The second approach is discussed in the DDEX SDK documentation, particularly in documentation for Sample Four.

Registration of a simple DDEX provider requires only the following information:

  1. A GUID that uniquely identifies the provider
  2. A string identifying the provider in the registry
  3. The invariant name of the ADO.NET provider

For example, the registry information could be as follows:

  1. {0EBAAB6E-CA80-4b4a-8DDF-CBE6BF058C77}
  2. ".NET Framework Data Provider for SQL Server"
  3. "System.Data.SqlClient"

Place this information in the Visual Studio registry root as follows:

The DDEX provider GUID is used as a sub-key under the "DataProviders" key (call this key the DDEX provider registry root).

DataProviders\{91510608-8809-4020-8897-FBA057E22D54}
   @=".NET Framework Data Provider for SQL Server"
   "Technology"="{77AB9A9D-78B9-4ba7-91AC-873F5338F1D2}"
   "InvariantName"="System.Data.SqlClient"

In addition to these registry entries, one more value is required: a value called "Technology". Place this value under the DDEX provider's registry root with the REG_SZ value "{77AB9A9D-78B9-4ba7-91AC-873F5338F1D2}". This value tells Visual Studio that the DDEX provider is based on an underlying provider that implements the ADO.NET object model.

Now we need to add registry entries that describe the capabilities of the provider. This information is added to the registry so that Visual Studio can query a provider for its capabilities without having to first load the provider.

First, we add a "SupportedObjects" key underneath the DDEX provider's registry root. Second, we add sub-keys for each supported object type. So far, we have a DDEX provider that only supports the capabilities of a standard ADO .NET 2.0-compliant data provider, so the following entries can be specified:

DataConnectionProperties
This indicates that connection string parsing and building is supported. The ADO.NET DbConnectionStringBuilder implementation is used to support this object.
DataConnectionSupport
This indicates that basic connection actions are supported, such as opening and closing connections. The ADO.NET DbConnection implementation is used to support this object.
DataSourceInformation
This indicates that certain generic information is available when connected to a data source. The ADO.NET DataSourceInformation metadata collection available through DbConnection.GetSchema() is used, amongst other things, to support this object.
DataCommand
This indicates that preparation and execution of commands on the data source are supported. The ADO.NET DbCommand implementation is used to support this object.
DataTransaction
This indicates that transactions on commands on the data source are supported. The ADO.NET DbTransaction implementation is used to support this object.

To complete our registration, we can optionally add some values underneath the "DataSourceInformation" key to specify static information customized for the provider. Each value name is the name of a data source information property, and each value is a REG_SZ literal representing the customized value. For example, a provider may specify a parameter prefix by inserting the value "ParameterPrefix" = ":" under this key.

This is all that is necessary to make a DDEX provider visible inside Visual Studio. At this level it will not provide very much design-time capability, but a DDEX client can enumerate its existence and use basic features as already supported through the ADO.NET provider (for example, building and opening connections and executing commands).

Integration with the connection dialog

One of the first tasks a user is faced with when working with data is how to provide information describing how to connect to their data. The data connection dialog in Visual Studio 2005 provides for this requirement by using a generic dialog box format whose content can be entirely customized by a DDEX provider.

The data connection dialog box relies on DDEX support of two objects: DataConnectionProperties and DataConnectionUIControl. The former provides methods for manipulating connection strings by editing their properties in a strongly typed manner. The latter provides a Windows Forms user control that is hosted by the dialog to supply customized UI, and is optional.

When this user control is not specified, the dialog hosts a property grid containing the properties exposed by the DataConnectionProperties object. When the DataConnectionProperties object is not supported, the dialog hosts a property grid containing a single property, ConnectionString, in which a user can directly enter a connection string.

In the section on registration it was shown that for an ADO.NET 2.0-compliant data provider, the DataConnectionProperties object is supported by default, by delegating calls to the ADO.NET provider's DbConnectionStringBuilder implementation. Hence a provider registered in this way will already work in the dialog. This section will show how to extend this to include implementation of a custom UI component.

Implementing the connection control

The first step when implementing the connection control is to create an assembly to contain the user control. To create this assembly, do the following:

  1. Create a new class library project in Visual Studio and add references to Microsoft.VisualStudio.Data.dll and Microsoft.Data.ConnectionUI.dll.
  2. Add a user control to the project.
  3. Close the designer for this user control and reopen the document in code view.
  4. Add a namespace using statement for Microsoft.VisualStudio.Data.
  5. Change the inheritance of the control from UserControl to DataConnectionUIControl.
  6. Build the solution.
  7. Reopen the designer for the user control.

At this point, the assembly contains a user control that inherits from the DDEX base type DataConnectionUIControl. The data connection dialog uses this base class to communicate with the user control.

Before populating this control, there are two important things to note. The first regards the contract between the user control and the hosting dialog box. The second regards style guidelines on the dialog box, to ensure a connection UI control does not look out of place when hosted in the dialog. The following paragraphs address these points.

UI Control Contract

There is a strict contract between the Data Connection dialog box and the data connection UI controls that it hosts. Controls are constructed on demand, as a user initiates an action that would cause the control to show. Immediately after construction, the control's Initialize method is called with a DataConnectionProperties instance to be used as the underlying store of properties surfaced by the control. These properties are available through the protected ConnectionProperties property. Then, at appropriate times, the LoadProperties method is called to indicate that current values stored in the DataConnectionProperties instance should be loaded into the controls on the user control.

UI Style Guidelines

Visual Studio has a set of recommended guidelines for layout and spacing of controls on dialog boxes. These guidelines have been followed by the data connection dialog and thus, to ensure neat integration with this dialog box, all user controls should observe these same guidelines. In addition to this, there are a couple of points of interest that will ensure that the user control looks correct when hosted by the dialog box:

  • Set the AutoScaleMode to Font. This will ensure that scaling of your control will occur in sync with the rest of the dialog.
  • Set the Margin to (0, 0, 0, 0) and when placing inner controls, allow them to touch the edges. For example, a label that should appear in the upper left corner of your user control should be located at position 0, 0 and a text box that should stretch the width of the user control should have a width equal to the width of the user control.
  • Set the Size to something greater than or equal to (300, 1). This is the minimum size allowed for user controls so any controls smaller than this will not fill all the space allocated for it.

Registering the UI control

Assuming that controls have been added to the data connection UI control, it is now necessary to register it. Given a basic registration as described in the registration section of this whitepaper, the following additional steps are required:

  • Associate the assembly containing the data connection UI control with the DDEX provider. This is done by adding a "Codebase" value under the DDEX provider's registry root; this "Codebase" value should point to the physical location on disk of the assembly DLL. For example, this might be "C:\MyProvider\MyProvider.dll".
  • Add DataConnectionUIControl as a supported object. See the Registration section on where to add this key.
  • Add information indicating the full name of the type in the assembly that implements the DataConnectionUIControl object. This should be added as the default value underneath the DataConnectionUIControl supported object key.

Once this registration is complete, your DDEX provider advertises that it supports the DataConnectionUIControl object and this will be created when the provider is selected in the data connection dialog box.

Integrating with the Server Explorer

The previous section described how to support design-time creation of connections with your provider. In this section we advance to the next step: how to represent your data source as a treeview hierarchy (or hierarchies) of object nodes in the Visual Studio Server Explorer.

Note   In some versions of Visual Studio 2005, Server Explorer is known by the alternate terms "Database Explorer" and "Data Explorer." In this whitepaper, however (as in the DDEX SDK documentation), you will find consistent use of "Server Explorer" to name the Visual Studio window in which source objects are depicted as a treeview of hierarchy nodes.

Server Explorer view relies on DDEX supporting two objects—DataObjectSupport and DataViewSupport. The DataObjectSupport object provides information in the form of an XML stream that describes the structure of the data source in terms of a flat object model. The DataViewSupport object, on the other hand, provides information, also as XML, that describes the hierarchical layout of selections of objects described by the data object support XML.

In essence, data object support exposes a raw data source, which may be a set of related metadata tables, as an object model, while the data view support represents the presentation of instances of such objects.

For the purposes of this whitepaper, we will limit ourselves to two types of object—a root object, which provides datasource-specific properties, and a relational table object.

Data object support

The first step is creating the data object support XML file. The skeleton structure of this XML is as follows:

Note   The structure of your data object support XML file must validate against the schema definition (.xsd) file named VsDataObjectSupport.xsd, which is provided with the DDEX SDK.
<?xml version="1.0" encoding="utf-8"?>
<VSDataObjectSupport xmlns="http://tempuri.org/VSDataObjectSupport.xsd">
   <Types>
   </Types>
</VSDataObjectSupport>

Underneath the <Types> element we will specify a RootType, followed by a Type for relational table:

<Types>
   <RootType>
      <Properties>
         <Property name="Server" type="System.String"/>
         <Property name="Login" type="System.String"/>
      </Properties>
      <Actions>
         <Action name="Enumerate"
            guid="61CC0372-384D-42e5-9707-6D7C8DC5287A"
            handler="Microsoft...AdoDotNetRootObjectEnumerator"/>
      </Actions>
   </RootType>
   <Type name="Table" defaultSort="Owner, Name">
      <Identifier>
         <Part name="Owner"/>
         <Part name="Name"/>
      </Identifier>
      <Properties>
         <Property name="Name" type="System.String"/>
         <Property name="Owner" type="System.String"/>
         <Property name="Type" type="System.String"/>
      </Properties>
      <Actions>
         <Action name="Enumerate"
            guid="61CC0372-384D-42e5-9707-6D7C8DC5287A"
            handler="Microsoft...AdoDotNetObjectEnumerator">
            <Parameter value="Tables"/>
         </Action>
         <Action name="BuildDSRef"
            guid="7C030900-E8DD-471b-8F18-D83DA7036144"
            handler="Microsoft.VisualStudio.Data.DSRefBuilder">
            <Parameter>
               <Parameter value="{1}"/>
               <Parameter value="{0}"/>
               <Parameter value="Table"/>
            </Parameter>
         </Action>
      </Actions>
   </Type>

</Types>

The root type specifies information about a singleton object type that supplies datasource-specific properties on the connection, such as a server name and login credentials. The root type is special in that it has no identifier (since there is only one instance, it has no need to be identified).

The table type provides information about an object type that describes a relational table. It has a two-part identifier; that is, it requires both an owner name plus a table name to uniquely identify an object instance on the data source.

Each type has a set of properties that are supplied by each object instance. These properties typically include a name, optionally followed by other parts of the identifier (for example, the owner), and followed by any other datasource-specific information about the object.

Each type also has an associated set of actions that can be performed for the type. These actions may apply to the type as a whole, such as how to enumerate instances of the type, or it may apply to a specific instance of the type, such as building a DSRef object. Each action has a user-friendly name, followed by a unique GUID identifier, followed by an identifier of a handler that handles the action. In Visual Studio 2005, two actions have been defined: enumerating objects and building a DSRef object for a given object instance.

For the root type, the enumeration handler is the Microsoft.VisualStudio.Data.AdoDotNet.AdoDotNetRootObjectEnumerator class. This enumerator provides the DataSource and Database properties from the ADO .NET DbConnection object, and enumerates values for each property in the connection string. There is no DSRef builder for this type as it does not represent any particular object for which a DSRef object could be created.

For the table type, the enumeration handler is the Microsoft.VisualStudio.Data.AdoDotNet.AdoDotNetObjectEnumerator class. This enumerator uses the ADO.NET provider's implementation of the DbConnection.GetSchema method to supply a particular schema collection. This handler is parameterized, meaning that within the XML, static information is passed as parameters to the handler specifying how it should behave. For this particular handler, it expects a single parameter indicating the name of the underlying collection name passed to the GetSchema method call. This example supposes that there is a collection called "Tables".

Data View Support

Now that we have a completed data object support XML file, let us move on to the data view support XML. The skeleton structure of this XML is as follows:

Note   The structure of your data view support XML file must validate against the schema definition (.xsd) file named VsDataViewSupport.xsd, which is provided with the DDEX SDK.
<?xml version="1.0" encoding="utf-8"?>
<VSDataViewSupport xmlns="http://tempuri.org/VSDataViewSupport.xsd">
   <DataViews>
   </DataViews>
</VSDataViewSupport>

Underneath the <DataViews> element we specify one <DataView> element, indicating one particular hierarchical layout of objects—a simple root node with a sub-folder for Tables, under which table objects are shown:

<DataViews>
   <DataView name="Default">
      <DisplayName>Default</DisplayName>
      <StaticConnectionNode>
         <InitialDisplayName>
            {Root.Server} ({Root.Login})
         </InitialDisplayName>
         <Children>
            <StaticNode nid="Tables">
               <DisplayName>Tables</DisplayName>
               <Children>
                  <Selection type="Table"/>
               </Children>
            </StaticNode>
         </Children>
      </StaticConnectionNode>
   </DataView>
</DataViews>

The example above is very simple. There is a static connection node, meaning a connection node that is not based on any particular underlying object. The initial display name of this connection node is the value of the Server property from the root object instance, followed by the value of the Login property from the root object instance in parenthesis. This information is identified using the data specifiers {Root.Server} and {Root.Login} respectively. Underneath the connection node is one child node—a static node labeled "Tables". This node has its own children, which are a selection of instances of table objects. Notice how a single <Selection> element describes the selection of objects to populate the hierarchy.

Registering object support and view support

Now that we have both data object and data view support XML files prepared, it is time to register them. Given a basic registration as described in the Registration section of this whitepaper, the following additional steps are required:

  1. Add DataObjectSupport as a supported object. See the registration section on where to add this key.
  2. Do either of these two:
    1. Add information indicating the physical location of the data object support XML file on disk. The Default value should be the unqualified XML file name, while the Path value should specify the full path to the file.

      — or —

    2. Add your XML as an embedded resource to your provider assembly. The default value in this case should be the full name of the resource in your assembly.
  3. Add DataViewSupport as a supported object, and register this in the same way as the DataObjectSupport, above.

Once this registration is complete, your DDEX provider advertises that it supports the DataObjectSupport and DataViewSupport objects and the provider will appear when selecting to add a new connection to Server Explorer. On adding a connection, objects will be populated in the tree view.

Integrating with project data design time

Up to this point, we have shown how to create and register a provider, supply UI for creating connections, and add a customized tree hierarchy in the Server Explorer. Next we want to show how to integrate with the design-time data features built into the Visual Studio project system.

The design-time data features that are part of the project system are inherently generic components; that is, they are data-related elements (like tables and columns) that are unaware of which data source they are talking to. This provides great flexibility, because it allows any DDEX provider to support the design-time without having to rewrite parts of the Visual Studio project system. On the other hand, however, this requires that the data object model supplied by the DDEX provider be provided in a generic manner so that Visual Studio understands it.

This is accomplished by using a DDEX feature called concept mapping.

The mappings indicate how the underlying object model of a data source maps to a set of fixed generic concepts, such as tables, views, stored procedures, and functions. A provider can support all, some, or none of these concepts. The project system will provide features for only those concepts that are supported. The data object support XML file discussed above allows you to define, or provide, these concept mappings.

There are two types of concepts: a type concept and a property concept. A type concept represents a generic object type, such as a Table. One or more types defined in your data object support XML can map to one or more type concepts. A property concept, on the other hand, represents a specific property on an object. One or more properties defined for a type in your data object support XML can map to one or more property concepts.

Mapping generic concepts

Let us take the table type that was discussed in the previous section. Recall that our table type has a two-part identifier consisting of an owner and a name, and has properties for name, owner, and type. We want to expose this type as the generic table concept recognized by generic clients.

First we specify the type concept mapping. This is done as the first element in the type, as follows:

<Type name="Table" defaultSort="Owner, Name">
   <Concepts>
      <Concept name="Table" restrictions="{Schema},{Name}"/>
   </Concepts>
</Type>

Since any underlying type can map to multiple type concepts, there is a <Concepts> element under which one or more <Concepts> elements can be specified. The <Concepts> element defines the name of a recognized type concept (in this case, Table) followed by a definition of how to map from generic restrictions on that type to the actual restrictions that must be passed to the type.

What does this mean? As with a regular client of the data object support XML, such as the Server Explorer, a generic client must be able to restrict an enumeration of objects using some generically known restriction set. For the table type concept, this is defined as a Catalog, Schema, and Name. The Catalog restriction does not make sense for this example, so it is ignored. However, the Schema really maps to our concept of Owner, and the Name obviously maps to Name. Thus, the above specification defines how, given the three generic restriction values, it maps into the two actual restriction values required for the underlying object enumerator. In cases where there is not a one-to-one mapping between restrictions, you can specify a filter on the Concept to allow for more complex restriction specifications.

After the type concept mapping, we move to the property concept mapping. For our example this is quite straightforward:

<Type name="Table" defaultSort="Owner, Name">
   <Properties>
      <Property name="Name" type="System.String"/>
      <Property name="Owner" type="System.String">
         <Concepts>
            <Concept name="Schema"/>
         </Concepts>
      </Property>
      <Property name="Type" type="System.String">
         <Concepts>
            <Concept name="Type">
               <Conversion>
                  <Calculate expr="IIF({0}='U','USER','SYSTEM')"/>
               </Conversion>
            </Concept>
         </Concepts>
      </Property>
   </Properties>
   ...
</Type>

There is a simple one-to-one mapping between the underlying "owner" notion and the generic "schema" concept. There is a slightly more interesting mapping between the table type information. Suppose the underlying table type returns either "U" for user or "S" for system; however, the generic table type property concept expects values in the form "USER" or "SYSTEM". Hence, there must be a conversion from "U" to "USER" and from "S" to "SYSTEM", respectively. You do this by using the simple conversion expression specified in the XML. At runtime, DDEX processes this expression and calculates the correct generic value based on the underlying value.

Now that our table type has generic concept mapping associated with it, it will become visible to generic project design-time data features such as the Data Source wizard. This whitepaper only demonstrates mapping of a table type, but depending on the complexity of the data source, there are many other concepts (described in the DDEX reference) that can be mapped to and utilized by Visual Studio.

Integrating with the Properties window from Server Explorer

Let us now turn back to the Server Explorer and provide more customization. Each node in the Server Explorer window can have properties, which are shown in the Visual Studio Properties window. By default, static nodes have no properties, but object (selection) nodes show all the properties on the underlying object enumerated from the data source, as defined in the data object support XML file.

There are two levels of customization supported for the Properties window: partial and full. Partial customization involves adding entries to the data view support XML file that extend the type information for a given object type to indicate which properties should be shown, and how. Full customization, on the other hand, involves implementing a browse object provider and attaching it to nodes in the explorer, so that at runtime a completely customized browse object is placed in the properties window.

The data view support XML file has a section called type extensions. The purpose of this section is to describe extensions to types defined in the data object support XML for the explicit purpose of supporting the Server Explorer.

Let us take again the Table type defined earlier and define a type extension for it:

<DataViews>
   <TypeExtensions>
      <TypeExtension name="Table">
         <DisplayName>Table</DisplayName>
         <Identifier>
            <Part name="Name">
               <DisplayName>(Name)</DisplayName>
               <Category>(Identity)</Category>
               <Description>
                  The name of the table.
               </Description>
            </Part>
         </Identifier>
         <Properties>
            <Property name="Owner">
               <DisplayName>Table Owner</DisplayName>
               <Category>Security</Category>
            </Property>
         </Properties>
      </TypeExtension>
   </TypeExtensions>
</DataViews>

In this XML example we have defined two properties that show up when a table node is selected. First is the Name part of the identifier, which has a display name of "(Name)" in a category "(Identity)" with the specified description. Second is the owner of the table, with a display name of "Table Owner" in the category "Security". While the data object support XML defines a Type property, this will not be shown because a type extension has been provided for the type and does not include this property in its list of displayable properties.

Type extensions work when specifying properties on object (selection) nodes, but there is no type extension for static nodes. Properties for these nodes are specified directly as part of the data view definition. For example, consider the children of the static connection node defined for the data view in the previous section:

<Children>
   <StaticNode nid="Tables">
      <DisplayName>Tables</DisplayName>
      <Properties>
         <Property>
            <DisplayName>My property</DisplayName>
            <Value>15</Value>
         </Property>
      </Properties>
      <Children>
         <Selection type="Table"/>
      </Children>
   </StaticNode>
</Children>

This example defines a simple custom property and value that is displayed when the Tables node is selected in the Server Explorer.

If you want to completely customize the content of the Properties window, you can supply a browse object provider as part of your type extension definition (or many places inline to a data view definition). Suppose we want a custom browse object for our Table type. Then we simply specify a value for the browseObjectProvider attribute on the <TypeExtension> element:

<TypeExtension name="Table" browseObjectProvider="MyAssembly.MyClass">

There is another attribute of interest—autoBrowseObject—that you can specify on the same element. The purpose of this element is to specify whether a default browse object should be built using information in the XML file, and then passed to the browse object provider for customization, or whether the browse object provider should be required to supply the whole browse object from scratch. The default value of this attribute is false.

Adding menu items in Server Explorer

This section outlines how to add custom menu items to context menus on object nodes in the Server Explorer. This is largely a function of two tasks:

  • Adding a command table .ctc file to a Visual Studio package satellite DLL and merging the information into the Visual Studio menu structure
  • Adding command binding information to the data view support XML file

Since this whitepaper is focused on DDEX, the first of these tasks will not be discussed in full. The VSIP Environment SDK documentation provides information on how to create a VS package and build and register a .ctc file.

The DDEX-specific portion of this task, on the other hand, involves deciding the placement of custom commands. The Server Explorer uses a fixed set of context menus for different types of node:

  • Connection: shown when the connection node is selected
  • Object: shown when a single object node is selected
  • Objects: shown when multiple object nodes of the same type are selected
  • Mixed Objects: shown when multiple object nodes of mixed types are selected
  • Static: shown when a single static node is selected
  • Statics: shown when multiple static nodes are selected
  • Mixed: shown when multiple object or static nodes are selected

These context menus are internally defined. Each context menu, however, contains a public group into which custom commands can be added. These are as follows:

  • guidVSData:IDG_DV_CONNECTION
  • guidVSData:IDG_DV_OBJECT
  • guidVSData:IDG_DV_OBJECTS
  • guidVSData:IDG_DV_MIXED_OBJECTS
  • guidVSData:IDG_DV_STATIC
  • guidVSData:IDG_DV_STATICS
  • guidVSData:IDG_DV_MIXED

The group GUID guidVSData is defined in vsshlids.h, while the IDs are defined in stdidcmd.h.

Assuming that a custom command has been defined and placed in one of the groups above, in the guidVSData:IDG_DV_OBJECT group, for example, then the following entry in the type extension for the Table type would bind this command to a data view command handler object that handles the command:

<TypeExtension name="Table">
   <CommandBindings>
      <CommandBinding
         name="MyCommand"
         guid="92A60F3D-C028-4624-8CF9-CB8058482082"
         cmdid="15"
         handler="MyAssembly.MyClass"/>
</TypeExtension>

This example demonstrates a command (arbitrarily named "MyCommand" for purpose of identifiability) in the XML file, with the specified group guid and command ID, and maps the handling of this command to the object MyAssembly.MyClass, which must inherit from the DataViewCommandHandler class.

Conclusion

Data Designer Extensibility (DDEX) provides a great mechanism for different data providers to integrate with Visual Studio 2005 and enable scenarios that allow developers to connect to and work with various data sources.

This paper has presented an introduction to DDEX and provided an overview of how to implement a few basic scenarios. Visual Studio 2005 ships with an SDK that includes DDEX. The SDK is accompanied by detailed documentation and samples. These should provide comprehensive details of how to implement your own DDEX provider.

Show:
© 2014 Microsoft