ADO.NET Orcas: Sample Provider

 

Last month, the ADO.NET team hosted a number of ADO.NET Data Provider writers on campus to present information on how to enhance an existing provider to support the Entity Framework features in the upcoming Orcas release. To help provider writers who were unable to attend, we’re publishing the material we presented to our team blog.

In this blog post, you’ll find information about how to extend an existing provider. The blog also has a .zip attachment that contains the slide decks presented at the event, a help file that covers the CommandTree class, and the source code for a sample ADO.NET Data Provider.

The sample provider extends the SqlClient provider that’s included with version 2.0 of the .NET Framework. It follows the steps listed in the blog post and includes code to handle SQL generation, commonly referred to as SQLGen. The SQLGen code uses the standard visitor pattern to generate a SqlCommand’s CommandText and Parameters collection based on the CommandTree supplied.

Provider writers can use the code in the provider to enhance and extend their existing providers. The sample provider targets Microsoft SQL Server 2005 and Microsoft SQL Express and contains the SQLGen logic similar to the logic inside of the version of SqlClient that will ship with Orcas. Provider writers can run Entity Framework scenarios with the sample provider, setting breakpoints and stepping into code to better understand the SQLGen logic – including what CommandTree the Entity Framework generated, how SqlClient converts the CommandTree into a SqlCommand.

This version of the sample provider is designed for the March CTP of Orcas. The ADO.NET team plans to update the sample provider for subsequent public releases of Orcas. Please note that the sample provider is not supported.

If you’re a provider writer working on enhancing your provider for Orcas and you’re not already in touch with the ADO.NET team, please let me know.

David Sceppa

ADO.NET Program Manager

David.Sceppa@microsoft.com

_________________________________________________________________________________________

This specification describes how to extend an existing ADO.NET Data Provider to support the Entity Framework in ADO.NET Orcas. Once a provider writer has extended their existing provider to support the Entity Framework, all Entity Framework scenarios will be supported using that provider (assuming the SQL Gen logic in the provider supports generating the appropriate store-specific queries) including generation of mapping files and classes via EdmGen.Exe.

1 Supporting instantiation via DbProviderFactories.GetFactory

Note: This section refers ADO.NET 2.0 features that the Entity Framework relies upon.

The primary Entity Framework scenarios involve supplying the provider's name as part of the connection string.

string providerConnectionString = @"Data Source=.SQLExpress;Initial Catalog=Northwind;" +

"Integrated Security=True";

string entityConnectionString =

string.Format(@"Metadata=.NorthwindModel.csdl|.NorthwindModel.ssdl|.NorthwindModel.msl;" +

"Provider=System.Data.SqlClient;" +

"Provider Connection String="{0}"", providerConnectionString);

using (EntityConnection connection = new EntityConnection(entityConnectionString))

{

}

The Entity Framework parses the connection string and extracts the provider's name and the connection string for the underlying provider, then instantiates the database connection using code like:

DbProviderFactory factory = DbProviderFactories.GetFactory(providerName);

DbConnection connection = factory.CreateConnection();

connection.ConnectionString = providerConnectionString;

This means that for a provider to plug into the Entity Framework, its factory class must be accessible via DbProviderFactories.GetFactory. Doing so has three main requirements:

1.1.1 The provider is listed in the combined configuration file

The provider may be listed in the machine configuration file, the application configuration file, etc. The provider's configuration entry should be under configuration / system.data / DbProviderFactories and typically looks like this.

 

<add name="SqlClient Data Provider"

     invariant="System.Data.SqlClient"

     description=".Net Framework Data Provider for SqlServer"

     type="System.Data.SqlClient.SqlClientFactory, System.Data, Version=2.0.0.0,

           Culture=neutral, PublicKeyToken=b77a5c561934e089" />

Note: The simple way to generate the value for the type attribute is to store typeof(ProviderFactory).AssemblyQualifiedName.

1.1.2 The provider's factory class is accessible via Type.GetType()

Calling DbProviderFactories.GetFactory also requires that provider factory's assembly qualified name can be used to access the provider factory class type via Type.GetType(). The two main ways to ensure that your provider can be instantiated in this fashion are to either register the assembly in the GAC (using regasm.exe) or to include the assembly in the application's directory.

1.1.3 The provider's ProviderFactory class has a public static Instance field

Each provider included with the .NET Framework exposes a public static Instance field on the provider factory class. ADO.NET relies on this pattern in the call to DbProviderFactories.GetFactory. If the desired provider does not follow this pattern, the call to GetFactory will throw an InvalidOperationException with the following message:

 

The requested .Net Framework Data Provider's implementation does not have an Instance field of a System.Data.Common.DbProviderFactory derived type.

2 Implementing ICloneable on the Command class

As part of the SQL Gen layer, described later, the Entity Framework requires that the underlying provider is able to generate a DbCommand given a CommandTree. The Entity Framework may execute the query specified in the DbCommand multiple times. To ensure that executing the query does not side effect the Command, the Entity Framework requires that the Command objects that a provider generates implements ICloneable and clones the Command and only uses the Execute methods of the cloned Commands.

Here is some sample code for cloning a Command object. The code assumes the SampleCommand already implements the ADO.NET 2.0 features and that the SampleParameter class follows a similar pattern for cloning.

 

public partial class SampleCommand : ICloneable

{

    object ICloneable.Clone()

    {

        SampleCommand clone = new SampleCommand();

        clone.Connection = this.Connection;

        clone.CommandText = this.CommandText;

        clone.CommandType = this.CommandType;

        clone.CommandTimeout = this.CommandTimeout;

        clone.DesignTimeVisible = this.DesignTimeVisible;

        clone.Transaction = this.Transaction;

        clone.UpdatedRowSource = this.UpdatedRowSource;

        foreach (SampleParameter p in this.Parameters)

            clone.Parameters.Add(((ICloneable) p).Clone());

        return clone;

    }

}

 

 

 

3 Overriding DbConnection.DbProviderFactory

There are many areas where the Entity Framework's API requires only a Connection object. However, the Entity Framework needs to access the ProviderFactory class for the provider given only an instance of its Connection class.

As of ADO.NET Orcas, there is no public way to access a ProviderFactory given a Connection. In ADO.NET Orcas, the DbConnection class has a new private ProviderFactory property, which calls a protected DbProviderFactory property. This is the same pattern established in ADO.NET 2.0 for properties like DbCommand.Connection. Note: The ProviderFactory property will likely be made public in a subsequent version of ADO.NET.