Share via


Working with DDEX Data Types and MappedTypes

The schema defines a single root element, DataObjectSupport, which bounds a hierarchy of elements that support external data object types in Visual Studio.

The DataObjectSupport Element

The DataObjectSupport element has four types of child elements:

  • Zero or more Import elements

  • Zero or more Define elements

  • Zero or one Types elements

  • Zero or one MappedTypes elements

The Import element and the Define element are provided to allow importing definitions from external data object support sources or to define elements that are reused in other parts of the XML, respectively, but they are not an integral part of defining data provider types and mapping types to generic types. They can be used to make authoring the data object support XML easier. However, the type-related elements consist of the Types element, which may contain a RootType and one or more Type elements, and the MappedTypes element, which may contain one or more MappedType elements. The set of Type elements define the object types exposed by the data provider and that are specific to the data source. These object types are mapped via the MappedType elements that link back to their corresponding Type element through the underlyingType attribute.

The following XML shows the layout of the Type and MappedType elements in relation to the DataObjectSupport top-level element.

<DataObjectSupport xmlns=http://.../DataObjectSupport.xsd>
    <Types>
        <RootType>
            ...
        </RootType>
        <Type name="Column">
            ...
        </Type>
        <Type name="View">
            ...
        </Type>
        ...
    </Types>
    <MappedTypes>
        <MappedType name="TableColumn" underlyingMember="Column">
            ...
        </MappedType>
        <MappedType name="View" underlyingMember="View">
            ...
        </MappedType>
        ...
    </MappedTypes>
</DataObjectSupport>

Type Element Children

The Type element has three allowable child elements:

  1. Identifier. Required. Specifies the unique identifier of the type. For example, a Table object might be identified by a combination of database, schema, and name. For a full discussion of identifiers and identifier parts, see Mapping Object Type Identifiers and Properties to Generic Types.

  2. Properties. Optional. Defines a collection of properties of a given object type, which are then specified using the Property element. For example, a Table object might have a CreateDate property.

  3. Services. Optional. Defines a collection of services, each specified by the Service element, that denote the services implemented for the type.

In addition, each type must have the following attributes:

  • The name attribute that represents the name of the object type.

Each type can optionally have the following attributes:

  • The nameProperty attribute if the object either (a) has no name property, or (b) has a name property with a value other than "name".

  • The preferredOrdering attribute that specificies the preferred sort order when selecting objects of the type.

Description of Type Elements and Attributes

Identifier Element

Type identifiers uniquely identify data objects returned from an enumeration of the collection of data source object types. The identifier is composed of data parts that allow callers to distinguish a specified object from others of the same type. For example, an identifier for a SQL Server database table is composed of a database name, schema name, and table name.

Note

Among object types, the root type is an exception to the identifier rule. The root type does not require an identifier because there is always only a single instance of the root type.

The identifier section of the type definition specifies a set of Part elements. Each part takes a required name attribute and an optional type attribute that specifies a .NET Framework type. If the type attribute is not specified, the default type is System.String. All of the Part elements taken together compose the unique identifier. The identifier is specified by the Identifier element. Optionally, an identifier can be defined once and then reused in the XML through the IdentifierRef element.

The following XML shows an example of a type identifier:

<Type name="Table">
    <Identifier>
        <Part name="Database" />
        <Part name="Schema" />
        <Part name="Name" />
    </Identifier>
</Type>

This code illustrates an XML description of a Table type that has a three-part identifier. Each Part element describes a particular portion of the identifier, and also includes a name attribute that references the data for the identifier portion of the specified object in the enumeration.

Properties Element

Object types can optionally have properties. The properties section of a given type definition describes name/type-value pairs that specify data for each property in an enumeration. The properties are specified using the Properties element, which groups individual Property elements. Each Property element in turn must contain a name attribute that specifies the name of the type and has an optional type attribute that specifiyies the .NET Framework type of the property. In addition, properties can be defined once and reused in the XML. This is achieved by adding a PropertyRef element to the Properties group element to reuse a defined element, or by adding a PropertyListRef element to reuse a group of previously defined properties.

The following code shows how property definitions might look for a type element.

<Define name="ColumnProperties">
   <Property name="Name" isIdentifierPart="true" />
   <Property name="Id" type="System.Int32" />
   <PropertyListRef name="DataTypeProperties" />
   <Property name="Nullable" type="System.Boolean" />
   <Property name="IsIdentity" type="System.Boolean" />
   <Property name="IdentitySeed" type="System.Int32" />
   <Property name="IdentityIncrement" type="System.Int32" />
</Define>
<Type name="Column" preferredOrdering="Database, Schema, Table, Id">
   <IdentifierRef name="SubSchemaObjectTypeIdentifier" arguments="Table" />
   <Properties>
      <PropertyListRef name="ColumnProperties" />
      <Property name="Computed" type="System.Boolean" />
   </Properties>
</Type>

The preceding code example shows the use of both an inline property definition for the Computed property and a reference to the previously defined list of column properties.

Property elements can be versioned, so that only specified properties are available for a given server version.

Services Element

Services can be defined for object types by adding each service as a Service element as part of the Services group. A service may be specific to the type, or a specialization of a more general service. You can specify the class that implements the service as a string in the implementationType attribute in the Service element, which can be resolved by the provider's object factory GetType() method (in IVsDataProviderObjectFactory). If the implementation type is not specified, the provider should supply a global implementation of the service in the IVsDataConnectionSupport support entity, which is implemented by the provider. Additionally, if parameters are specified for the service, the service should also implement the IVsDataObjectSupport support entity.

The following code shows how to define the IVsDataObjectSelector service:

<Type name="Column" preferredOrdering="Database, Schema, Table, Id">
   <IdentifierRef name="SubSchemaObjectTypeIdentifier" arguments="Table" />
   <Properties>
   ...
   </Properties>
   <Services>
      <Service type="IVsDataObjectSelector">
         <Parameters method="SelectObjects">
            <Parameter>
               <ParameterRef name="UrnPart" arguments="Database, 0" />
               <ParameterRef name="UrnPartWithSchema" arguments="$(parentUrnPartName), 1, 2" />
               <ParameterRef name="UrnPart" arguments="$(urnPartName), 3"/>
            </Parameter>
            <Parameter>
               <ParameterRef name="SelectorMapping" arguments="Database, Database_Name" />
               <ParameterRef name="SelectorMapping" arguments="Schema, $(parentUrnPartName)_Schema" />
               <ParameterRef name="SelectorMapping" arguments="$(parentType), $(parentUrnPartName)_Name" />
               <ParameterListRef name="$(selectorMappings)" />
            </Parameter>
         </Parameters>
      </Service>
      ...
   </Services>
</Type>

Note in the preceding code example that ParameterRef points to a previously defined parameter using the Define element. Similarly, the ParameterListRef points to a group of previously defined parameters. These definitions are not included in the code example.

name Attribute

The name attribute specifies the name of the object type.

nameProperty Attribute

Object types can define a property that specifies the unqualified name of the object's property by using the nameProperty attribute. This attribute specifies the name of a property defined on the type that represents the unqualified name of instances of the type. If unspecified, a property called "Name" will be chosen if it exists, otherwise instances of the type are considered nameless.

preferredOrdering Attribute

Types can use the preferredOrdering attribute to specify the sort order on objects returned from the object store of the given type.

Typically, the sort is ascending on various identifier parts, but it need not be. In some situations, you may want to sort on a more logical criterion, for example, providing ordinal ordering of columns rather than alphabetical ordering by column name.

MappedType Element Children

The MappedType element has three allowable child elements:

  1. Selection. Required. Contains the mapping information that allows returning underlying objects from the data source corresponding to the object selection of mapped objects.

  2. Identifier. Optional. Specifies the unique identifier of the mapped type and provides the mapping to the data source–specific type identifier.

  3. Properties. Optional. Defines a collection of properties of a mapped object type, which are then specified using the Property element. For example, a Table generic object might have a CreateDate generic property.

In addition, each type must have the following attributes:

  • The name attribute that represents the name of the mapped object type.

Each type can optionally have the following attributes:

  • The underlyingType attribute that links the generic mapped type to its corresponding data source–specific type.

Description of MappedType Elements and Attributes

Selection Element

The Selection element allows selection of objects from the data source by providing the mapping information to restrict the data returned from the object store. The restrictions attribute contains identifier restrictions used to limit the amount of objects returned in a selection call. Note that property restrictions are not supported in the current version of DDEX. If there is no one-to-one mapping between a generic restriction and an underlying identifier part, SubstitutionValue elements should be added in the SubstitutionValues group to perform the necessary conversion; the identifier part in this case should be replaced by {n}, where n is the zero-based integer index of the substitution value. Furthermore, if there is no one-to-one mapping between the generic mapped type and the underlying data source–specific type, a filter attribute may be added to add an additional filtering condition to further narrow down the set of returned objects. In addition, to specify the ordering of the returned objects from the data source, the ordering attribute may be added.

The following code example illustrates the use of the Selection element:

<MappedType name="Table" underlyingType="Table">
   <Selection restrictions="{Catalog},{Schema},{Name}" />
   ...
</MappedType>

Identifier Element

Specifies the unique identifier of the mapped type and provides the mapping to the data source–specific type identifier. The identifier is a combination of identifier parts, which are database objects that together uniquely identify the mapped type. Each Part element may contain conversions, represented as conversion steps in the Conversion element, that are necessary to map the generic identifier part value to the data source identifier part value.

The following code example illustrates the use of the Identifier and Part elements.

<MappedType name="Table" underlyingType="Table">
   <Identifier>
      <Part name="Catalog" underlyingMember="Database" />
      <Part name="Schema" underlyingMember="Schema" />
      <Part name="Name" underlyingMember="Name" />
   </Identifier>
</MappedType>

Properties Element

The properties of a mapped type describe the generic properties that are mapped to the data source–specific properties via the underlyingMember attribute in the Property element. Each property has a required name attribute that specifies the name of the generic property.The isIdentifierPart attribute indicates whether the property in question corresponds to an identifier part with the same name. To convert property values from the data source–specific property to the generic property, the property may contain conversions, represented as conversion steps in the Conversion element.

In addition, properties can be defined once and reused in the XML. This is achieved by adding a PropertyRef element to the Properties group element to reuse a defined element, or by adding a PropertyListRef element to reuse a group of previously defined properties.

The following code shows how property definitions might look for a mapped type element.

<MappedType name="TableColumn" underlyingType="Column">
   <Selection restrictions="{Catalog},{Schema},{Table},{Name}" />
   <IdentifierRef name="MappedSubTypeIdentifier" arguments="Table" />
   <Properties>
      <Property name="Name" isIdentifierPart="true" />
      <Property name="Ordinal" underlyingMember="ID" />
      <Property name="DataType" underlyingMember="DataType" />
      <Property name="IsNullable" underlyingMember="Nullable" />
      <Property name="IsComputed" underlyingMember="Computed" />
   </Properties>
</MappedType>

name Attribute

The name attribute specifies the name of the mapped object type.

underlyingType Attribute

This attribute specifies the name of the underlying data source–specific type from which the mapped object type will retrieve its members.

DDEX Data Object Type and MappedType Functions

Data object types have four primary functions. They describe:

  1. How to accept an enumeration request from the Visual Studio metadata engine and convert it to an equivalent request using the underlying object selection interface (IVsDataObjectSelector in the Microsoft.VisualStudio.Data.Services.SupportEntities namespace).

  2. The data returned by the underlying object selection technology in terms of an identifier and properties.

  3. How to build a DSRef object by using the IDSRefBuilder interface.

  4. The type, identifier, and properties in a generic fashion for the mapped type, where appropriate.

The four functions listed above are discussed in detail in the following topics: