Provider Manifest Specification

This section discusses how a data store provider can support the types and functions in the data store.

Entity Services operates independently of a specific data store provider yet still allows a data provider to explicitly define how models, mappings, and queries interact with an underlying data store. Without a layer of abstraction, Entity Services could only be targeted at a specific data store or data provider.

Types that the provider supports are directly or indirectly supported by the underlying database. These types are not necessarily the exact store types, but the types the provider uses to support the Entity Framework. Provider/store types are described in the Entity Data Model (EDM) terms.

Parameter and return types for the functions supported by the data store are specified in EDM terms.

Requirements

The Entity Framework and the data store need to be able to pass data back and forth in known types without any data loss or truncation.

The provider manifest must be loadable by tools at design time without having to open a connection to the data store.

The Entity Framework is case sensitive, but the underlying data store may not be. When EDM artifacts (identifiers and type names, for example) are defined and used in the manifest, they must use the Entity Framework case sensitivity. If data store elements that may be case sensitive appear in the provider manifest, that casing needs to be maintained in the provider manifest.

The Entity Framework requires a provider manifest for all data providers. If you try to use a provider that does not have a provider manifest with the Entity Framework, you will get an error.

The following table describes the kinds of exceptions the Entity Framework would throw when exceptions arise through provider interaction:

Issue Exception

The Provider does not support GetProviderManifest in DbProviderServices.

ProviderIncompatibleException

Missing provider manifest: the provider returns null when attempting to retrieve the provider manifest.

ProviderIncompatibleException

Invalid provider manifest: the provider returns invalid XML when attempting to retrieve the provider manifest.

ProviderIncompatibleException

Scenarios

A provider should support the following scenarios:

Writing a Provider with Symmetric Type Mapping

You can write a provider for the Entity Framework where each store type maps to a single EDM type, regardless of the mapping direction. For a provider type that has very simple mapping that corresponds with an EDM type, you can use a symmetric solution because the type system is simple or matches EDM types.

You can use the simplicity of their domain and produce a static declarative provider manifest.

You write an XML file that has two sections:

  • A list of provider types expressed in terms of the "EDM counterpart" of a store type or function. Store types have counterpart EDM types. Store functions have corresponding EDM functions. For example, varchar is a SQL Server type but the corresponding EDM type is string.

  • A list of functions supported by the provider where parameter and return types are expressed in EDM terms.

Writing a Provider with Asymmetric Type Mapping

When writing a data store provider for the Entity Framework, the EDM-to-provider type mapping for some types may be different from provider-to-EDM type mapping. For instance, unbounded EDM PrimitiveTypeKind.String may map to nvarchar(4000) on the provider, while nvarchar(4000) maps to the EDM PrimitiveTypeKind.String(MaxLength=4000).

You write an XML file that has two sections:

  • A list of provider types expressed in EDM terms and define mapping for both direction: EDM-to-provider and provider-to-EDM.

  • A list of functions supported by the provider where parameter and return types are expressed in EDM terms.

Provider Manifest Discoverability

The manifest is used indirectly by several component types in Entity Services (for example Tools or Query) but more directly leveraged by metadata through the use of the data store metadata loader.

dfb3d02b-7a8c-4d51-ac5a-a73d8aa145e6

However, a given provider may support different stores or different versions of the same store. Therefore, a provider must report a different manifest for each supported data store.

Provider Manifest Token

When a data store connection is opened, the provider can query for information to return the right manifest. This may not be possible in offline scenarios where connection information is not available or when it is not possible to connect to the store. Identify the manifest by using the ProviderManifestToken attribute of the Schema element in the .ssdl file. There is no required format for this attribute; the provider chooses the minimum information needed to identify a manifest without opening a connection to the store.

For example:

<Schema Namespace="Northwind" Provider="System.Data.SqlClient" ProviderManifestToken="2005" xmlns:edm="https://schemas.microsoft.com/ado/2006/04/edm/ssdl" xmlns="https://schemas.microsoft.com/ado/2006/04/edm/ssdl">

Provider Manifest Programming Model

Providers derive from DbXmlEnabledProviderManifest, which allows them to specify their manifests declaratively. The following illustration shows the class hierarchy of a provider:

None

Discoverability API

The provider manifest is loaded by the Store Metadata loader (StoreItemCollection), either by using a data store connection or a provider manifest token.

Using a Data Store Connection

When the data store connection is available, call DbProvderServices.GetProviderManifestToken to return the token that is passed to the GetProviderManifest method, which returns DbProviderManifest. This method delegates to the provider's implementation of GetDbProviderManifestToken.

public string GetProviderManifestToken(DbConnection connection);
public DbProviderManifest GetProviderManifest(string manifestToken);

Using a Provider Manifest Token

For the offline scenario, the token is picked from SSDL representation. The SSDL allows you to specify a ProviderManifestToken (see Schema Element (SSDL) for more information). For example, if a connection cannot be opened, the SSDL has a provider manifest token that specifies information about the manifest.

public DbProviderManifest GetProviderManifest(string manifestToken);

Provider Manifest Schema

The schema of information defined for each provider contains the static information to be consumed by metadata:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema elementFormDefault="qualified"
   xmlns:xs="http://www.w3.org/2001/XMLSchema"
   targetNamespace="https://schemas.microsoft.com/ado/2006/04/edm/providermanifest"
   xmlns:pm="https://schemas.microsoft.com/ado/2006/04/edm/providermanifest">

  <xs:element name="ProviderManifest">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="Types" type="pm:TTypes" minOccurs="1" maxOccurs="1" />
        <xs:element name="Functions" type="pm:TFunctions" minOccurs="0" maxOccurs="1"/>
      </xs:sequence>
      <xs:attribute name="Namespace" type="xs:string" use="required"/>
    </xs:complexType>
  </xs:element>
  <xs:complexType name="TVersion">
    <xs:attribute name="Major" type="xs:int" use="required" />
    <xs:attribute name="Minor" type="xs:int" use="required" />
    <xs:attribute name="Build" type="xs:int" use="required" />
    <xs:attribute name="Revision" type="xs:int" use="required" />
  </xs:complexType>

  <xs:complexType name="TIntegerFacetDescription">
    <xs:attribute name="Minimum" type="xs:int" use="optional" />
    <xs:attribute name="Maximum" type="xs:int" use="optional" />
    <xs:attribute name="DefaultValue" type="xs:int" use="optional" />
    <xs:attribute name="Constant" type="xs:boolean" default="false" />
  </xs:complexType>

  <xs:complexType name="TBooleanFacetDescription">
    <xs:attribute name="DefaultValue" type="xs:boolean" use="optional" />
    <xs:attribute name="Constant" type="xs:boolean" default="true" />
  </xs:complexType>

  <xs:complexType name="TDateTimeFacetDescription">
    <xs:attribute name="Constant" type="xs:boolean" default="false" />
  </xs:complexType>

  <xs:complexType name="TFacetDescriptions">
    <xs:choice maxOccurs="unbounded">
      <xs:element name="Precision" minOccurs="0" maxOccurs="1" type="pm:TIntegerFacetDescription"/>
      <xs:element name="Scale" minOccurs="0" maxOccurs="1" type="pm:TIntegerFacetDescription"/>
      <xs:element name="MaxLength" minOccurs="0" maxOccurs="1" type="pm:TIntegerFacetDescription"/>
      <xs:element name="Unicode" minOccurs="0" maxOccurs="1" type="pm:TBooleanFacetDescription"/>
      <xs:element name="FixedLength" minOccurs="0" maxOccurs="1" type="pm:TBooleanFacetDescription"/>
    </xs:choice>
  </xs:complexType>

  <xs:complexType name="TType">
    <xs:sequence>
      <xs:element name="FacetDescriptions" type="pm:TFacetDescriptions" minOccurs="0" maxOccurs="1"/>
    </xs:sequence>
    <xs:attribute name="Name" type="xs:string" use="required"/>
    <xs:attribute name="PrimitiveTypeKind" type="pm:TPrimitiveTypeKind" use="required" />
  </xs:complexType>

  <xs:complexType name="TTypes">
    <xs:sequence>
      <xs:element name="Type" type="pm:TType" minOccurs="0" maxOccurs="unbounded"/>
    </xs:sequence>
  </xs:complexType>

  <xs:attributeGroup name="TFacetAttribute">
    <xs:attribute name="Precision" type="xs:int" use="optional"/>
    <xs:attribute name="Scale" type="xs:int" use="optional"/>
    <xs:attribute name="MaxLength" type="xs:int" use="optional"/>
    <xs:attribute name="Unicode" type="xs:boolean" use="optional"/>
    <xs:attribute name="FixedLength" type="xs:boolean" use="optional"/>
  </xs:attributeGroup>

  <xs:complexType name="TFunctionParameter">
    <xs:attribute name="Name" type="xs:string" use="required" />
    <xs:attribute name="Type" type="xs:string" use="required" />
    <xs:attributeGroup ref="pm:TFacetAttribute" />
    <xs:attribute name="Mode" type="pm:TParameterDirection" use="required" />
  </xs:complexType>

  <xs:complexType name="TReturnType">
    <xs:attribute name="Type" type="xs:string" use="required" />
    <xs:attributeGroup ref="pm:TFacetAttribute" />
  </xs:complexType>

  <xs:complexType name="TFunction">
    <xs:choice minOccurs="0" maxOccurs ="unbounded">
      <xs:element name ="ReturnType" type="pm:TReturnType" minOccurs="0" maxOccurs="1" />
      <xs:element name="Parameter" type="pm:TFunctionParameter" minOccurs="0" maxOccurs="unbounded"/>
    </xs:choice>
    <xs:attribute name="Name" type="xs:string" use="required" />
    <xs:attribute name="Aggregate" type="xs:boolean" use="optional" />
    <xs:attribute name="BuiltIn" type="xs:boolean" use="optional" />
    <xs:attribute name="StoreFunctionName" type="xs:string" use="optional" />
    <xs:attribute name="NiladicFunction" type="xs:boolean" use="optional" />
    <xs:attribute name="ParameterTypeSemantics" type="pm:TParameterTypeSemantics" use="optional" default="AllowImplicitConversion" />
  </xs:complexType>

  <xs:complexType name="TFunctions">
    <xs:sequence>
      <xs:element name="Function" type="pm:TFunction" minOccurs="0" maxOccurs="unbounded"/>
    </xs:sequence>
  </xs:complexType>

  <xs:simpleType name="TPrimitiveTypeKind">
    <xs:restriction base="xs:string">
      <xs:enumeration value="Binary"/>
      <xs:enumeration value="Boolean"/>
      <xs:enumeration value="Byte"/>
      <xs:enumeration value="Decimal"/>
      <xs:enumeration value="DateTime"/>
      <xs:enumeration value="Time"/>
      <xs:enumeration value="DateTimeOffset"/>        
      <xs:enumeration value="Double"/>
      <xs:enumeration value="Guid"/>
      <xs:enumeration value="Single"/>
      <xs:enumeration value="SByte"/>
      <xs:enumeration value="Int16"/>
      <xs:enumeration value="Int32"/>
      <xs:enumeration value="Int64"/>
      <xs:enumeration value="String"/>
    </xs:restriction>
  </xs:simpleType>


  <xs:simpleType name="TParameterDirection">
    <xs:restriction base="xs:string">
      <xs:enumeration value="In"/>
      <xs:enumeration value="Out"/>
      <xs:enumeration value="InOut"/>
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="TParameterTypeSemantics">
    <xs:restriction base="xs:string">
      <xs:enumeration value="ExactMatchOnly" />
      <xs:enumeration value="AllowImplicitPromotion" />
      <xs:enumeration value="AllowImplicitConversion" />
    </xs:restriction>
  </xs:simpleType>
</xs:schema>

Types Node

The Types node in the provider manifest contains information about the Types that are supported natively by the data store or through the provider.

Type Node

Each Type node defines a provider type in terms of EDM. The Type node describes the name of the provider type, and information related to the model type it maps to and facets to describe that type mapping.

In order to express this type information in the provider manifest, each TypeInformation declaration must define several facet descriptions for each Type:

Attribute Name Data Type Required Default Value Description

Name

String

Yes

n/a

Provider-specific data type name

PrimitiveTypeKind

PrimitiveTypeKind

Yes

n/a

EDM type name

Function Node

Each Function defines a single function available through the provider.

Attribute Name Data Type Required Default Value Description

Name

String

Yes

n/a

Identifier/name of the function

ReturnType

String

No

Void

The EDM return type of the function

Aggregate

Boolean

No

False

True if the function is an aggregate function

BuiltIn

Boolean

No

True

True if the function is built into the data store

StoreFunctionName

String

No

<Name>

Function Name in the data store. Allows for a level of redirection of function names.

NiladicFunction

Boolean

No

False

True if the function does not require parameters and is called without any parameters

ParameterType

Semantics

ParameterSemantics

No

AllowImplicit

Conversion

Choice of how the query pipeline should deal with parameter type substitution:

  • ExactMatchOnly

  • AllowImplicitPromotion

  • AllowImplicitConversion

Parameters Node

Each function has a collection of one or more Parameter nodes.

Attribute Name Data Type Required Default Value Description

Name

String

Yes

n/a

Identifier/name of the parameter.

Type

String

Yes

n/a

The EDM type of the parameter.

Mode

Parameter

Direction

Yes

n/a

Direction of parameter:

  • in

  • out

  • inout

Namespace Attribute

Each data store provider must define a namespace or group of namespaces for information defined in the manifest. This namespace can be used in Entity SQL queries to resolve names of functions and types. For instance: SqlServer. That namespace must be different from the canonical namespace, EDM, defined by Entity Services for standard functions to be supported by Entity SQL queries.

See Also

Concepts

Writing an Entity Framework Data Provider