//------------------------------------------------------------------------------ // <copyright file="DbProviderServices.cs" company="Microsoft"> // Copyright (c) Microsoft Corporation. All rights reserved. // </copyright> // // @owner Microsoft // @backupOwner Microsoft //------------------------------------------------------------------------------ using System.Data.SqlClient; namespace System.Data.Common { using System.Data.Common.CommandTrees; using System.Data.Entity; using System.Data.Metadata.Edm; using System.Data.Spatial; using System.Diagnostics; using System.IO; using System.Reflection; using System.Xml; /// <summary> /// The factory for building command definitions; use the type of this object /// as the argument to the IServiceProvider.GetService method on the provider /// factory; /// </summary> [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Db")] [CLSCompliant(false)] abstract public class DbProviderServices { /// <summary> /// Create a Command Definition object given a command tree. /// </summary> /// <param name="commandTree">command tree for the statement</param> /// <returns>an exectable command definition object</returns> /// <remarks> /// This method simply delegates to the provider's implementation of CreateDbCommandDefinition. /// </remarks> public DbCommandDefinition CreateCommandDefinition(DbCommandTree commandTree) { EntityUtil.CheckArgumentNull(commandTree, "commandTree"); ValidateDataSpace(commandTree); StoreItemCollection storeMetadata = (StoreItemCollection)commandTree.MetadataWorkspace.GetItemCollection(DataSpace.SSpace); Debug.Assert(storeMetadata.StoreProviderManifest != null, "StoreItemCollection has null StoreProviderManifest?"); return CreateDbCommandDefinition(storeMetadata.StoreProviderManifest, commandTree); } /// <summary> /// Create a Command Definition object given a command tree. /// </summary> /// <param name="commandTree">command tree for the statement</param> /// <returns>an exectable command definition object</returns> /// <remarks> /// This method simply delegates to the provider's implementation of CreateDbCommandDefinition. /// </remarks> public DbCommandDefinition CreateCommandDefinition(DbProviderManifest providerManifest, DbCommandTree commandTree) { try { return CreateDbCommandDefinition(providerManifest, commandTree); } catch (ProviderIncompatibleException) { throw; } catch (Exception e) { if (EntityUtil.IsCatchableExceptionType(e)) { throw EntityUtil.ProviderIncompatible(Strings.ProviderDidNotCreateACommandDefinition, e); } throw; } } /// <summary> /// Create a Command Definition object, given the provider manifest and command tree /// </summary> /// <param name="connection">provider manifest previously retrieved from the store provider</param> /// <param name="commandTree">command tree for the statement</param> /// <returns>an exectable command definition object</returns> [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Db")] protected abstract DbCommandDefinition CreateDbCommandDefinition(DbProviderManifest providerManifest, DbCommandTree commandTree); /// <summary> /// Ensures that the data space of the specified command tree is the target (S-) space /// </summary> /// <param name="commandTree">The command tree for which the data space should be validated</param> internal virtual void ValidateDataSpace(DbCommandTree commandTree) { Debug.Assert(commandTree != null, "Ensure command tree is non-null before calling ValidateDataSpace"); if (commandTree.DataSpace != DataSpace.SSpace) { throw EntityUtil.ProviderIncompatible(Strings.ProviderRequiresStoreCommandTree); } } /// <summary> /// Create a DbCommand object given a command tree. /// </summary> /// <param name="commandTree">command tree for the statement</param> /// <returns>a command object</returns> internal virtual DbCommand CreateCommand(DbCommandTree commandTree) { DbCommandDefinition commandDefinition = CreateCommandDefinition(commandTree); DbCommand command = commandDefinition.CreateCommand(); return command; } /// <summary> /// Create the default DbCommandDefinition object based on the prototype command /// This method is intended for provider writers to build a default command definition /// from a command. /// Note: This will clone the prototype /// </summary> /// <param name="prototype">the prototype command</param> /// <returns>an executable command definition object</returns> public virtual DbCommandDefinition CreateCommandDefinition(DbCommand prototype) { return DbCommandDefinition.CreateCommandDefinition(prototype); } /// <summary> /// Retrieve the provider manifest token based on the specified connection. /// </summary> /// <param name="connection">The connection for which the provider manifest token should be retrieved.</param> /// <returns> /// The provider manifest token that describes the specified connection, as determined by the provider. /// </returns> /// <remarks> /// This method simply delegates to the provider's implementation of GetDbProviderManifestToken. /// </remarks> public string GetProviderManifestToken(DbConnection connection) { try { string providerManifestToken = GetDbProviderManifestToken(connection); if (providerManifestToken == null) { throw EntityUtil.ProviderIncompatible(Strings.ProviderDidNotReturnAProviderManifestToken); } return providerManifestToken; } catch (ProviderIncompatibleException) { throw; } catch (Exception e) { if (EntityUtil.IsCatchableExceptionType(e)) { throw EntityUtil.ProviderIncompatible(Strings.ProviderDidNotReturnAProviderManifestToken, e); } throw; } } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Db")] protected abstract string GetDbProviderManifestToken(DbConnection connection); public DbProviderManifest GetProviderManifest(string manifestToken) { try { DbProviderManifest providerManifest = GetDbProviderManifest(manifestToken); if (providerManifest == null) { throw EntityUtil.ProviderIncompatible(Strings.ProviderDidNotReturnAProviderManifest); } return providerManifest; } catch (ProviderIncompatibleException) { throw; } catch (Exception e) { if (EntityUtil.IsCatchableExceptionType(e)) { throw EntityUtil.ProviderIncompatible(System.Data.Entity.Strings.ProviderDidNotReturnAProviderManifest, e); } throw; } } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Db")] protected abstract DbProviderManifest GetDbProviderManifest(string manifestToken); public DbSpatialDataReader GetSpatialDataReader(DbDataReader fromReader, string manifestToken) { try { DbSpatialDataReader spatialReader = GetDbSpatialDataReader(fromReader, manifestToken); if (spatialReader == null) { throw EntityUtil.ProviderIncompatible(Strings.ProviderDidNotReturnSpatialServices); } return spatialReader; } catch (ProviderIncompatibleException) { throw; } catch (Exception e) { if (EntityUtil.IsCatchableExceptionType(e)) { throw EntityUtil.ProviderIncompatible(System.Data.Entity.Strings.ProviderDidNotReturnSpatialServices, e); } throw; } } public DbSpatialServices GetSpatialServices(string manifestToken) { try { DbSpatialServices spatialServices = DbGetSpatialServices(manifestToken); if (spatialServices == null) { throw EntityUtil.ProviderIncompatible(Strings.ProviderDidNotReturnSpatialServices); } return spatialServices; } catch (ProviderIncompatibleException) { throw; } catch (Exception e) { if (EntityUtil.IsCatchableExceptionType(e)) { throw EntityUtil.ProviderIncompatible(System.Data.Entity.Strings.ProviderDidNotReturnSpatialServices, e); } throw; } } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Db")] protected virtual DbSpatialDataReader GetDbSpatialDataReader(DbDataReader fromReader, string manifestToken) { // Must be a virtual method; abstract would break previous implementors of DbProviderServices throw EntityUtil.ProviderIncompatible(Strings.ProviderDidNotReturnSpatialServices); } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Db")] protected virtual DbSpatialServices DbGetSpatialServices(string manifestToken) { // Must be a virtual method; abstract would break previous implementors of DbProviderServices throw EntityUtil.ProviderIncompatible(Strings.ProviderDidNotReturnSpatialServices); } internal void SetParameterValue(DbParameter parameter, TypeUsage parameterType, object value) { Debug.Assert(parameter != null, "Validate parameter before calling SetParameterValue"); Debug.Assert(parameterType != null, "Validate parameterType before calling SetParameterValue"); this.SetDbParameterValue(parameter, parameterType, value); } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Db")] protected virtual void SetDbParameterValue(DbParameter parameter, TypeUsage parameterType, object value) { EntityUtil.CheckArgumentNull(parameter, "parameter"); EntityUtil.CheckArgumentNull(parameterType, "parameterType"); parameter.Value = value; } /// <summary> /// Create an instance of DbProviderServices based on the supplied DbConnection /// </summary> /// <param name="connection">The DbConnection to use</param> /// <returns>An instance of DbProviderServices</returns> public static DbProviderServices GetProviderServices(DbConnection connection) { return GetProviderServices(GetProviderFactory(connection)); } internal static DbProviderFactory GetProviderFactory(string providerInvariantName) { EntityUtil.CheckArgumentNull(providerInvariantName, "providerInvariantName"); DbProviderFactory factory; try { factory = DbProviderFactories.GetFactory(providerInvariantName); } catch (ArgumentException e) { throw EntityUtil.Argument(Strings.EntityClient_InvalidStoreProvider, e); } return factory; } /// <summary> /// Retrieve the DbProviderFactory based on the specified DbConnection /// </summary> /// <param name="connection">The DbConnection to use</param> /// <returns>An instance of DbProviderFactory</returns> public static DbProviderFactory GetProviderFactory(DbConnection connection) { EntityUtil.CheckArgumentNull(connection, "connection"); DbProviderFactory factory = DbProviderFactories.GetFactory(connection); if (factory == null) { throw EntityUtil.ProviderIncompatible(System.Data.Entity.Strings.EntityClient_ReturnedNullOnProviderMethod( "get_ProviderFactory", connection.GetType().ToString())); } Debug.Assert(factory != null, "Should have thrown on null"); return factory; } internal static DbProviderServices GetProviderServices(DbProviderFactory factory) { EntityUtil.CheckArgumentNull(factory, "factory"); // Special case SQL client so that it will work with System.Data from .NET 4.0 even without // a binding redirect. if (factory is SqlClientFactory) { return SqlProviderServices.Instance; } IServiceProvider serviceProvider = factory as IServiceProvider; if (serviceProvider == null) { throw EntityUtil.ProviderIncompatible(System.Data.Entity.Strings.EntityClient_DoesNotImplementIServiceProvider( factory.GetType().ToString())); } DbProviderServices providerServices = serviceProvider.GetService(typeof(DbProviderServices)) as DbProviderServices; if (providerServices == null) { throw EntityUtil.ProviderIncompatible( System.Data.Entity.Strings.EntityClient_ReturnedNullOnProviderMethod( "GetService", factory.GetType().ToString())); } return providerServices; } /// <summary> /// Return an XML reader which represents the CSDL description /// </summary> /// <returns>An XmlReader that represents the CSDL description</returns> internal static XmlReader GetConceptualSchemaDefinition(string csdlName) { return DbProviderServices.GetXmlResource("System.Data.Resources.DbProviderServices." + csdlName + ".csdl"); } internal static XmlReader GetXmlResource(string resourceName) { Assembly executingAssembly = Assembly.GetExecutingAssembly(); Stream stream = executingAssembly.GetManifestResourceStream(resourceName); return XmlReader.Create(stream, null, resourceName); } /// <summary> /// Generates a DDL script which creates schema objects (tables, primary keys, foreign keys) /// based on the contents of the storeItemCollection and targeted for the version of the backend corresponding to /// the providerManifestToken. /// Individual statements should be separated using database-specific DDL command separator. /// It is expected that the generated script would be executed in the context of existing database with /// sufficient permissions, and it should not include commands to create the database, but it may include /// commands to create schemas and other auxiliary objects such as sequences, etc. /// </summary> /// <param name="providerManifestToken">The provider manifest token identifying the target version</param> /// <param name="storeItemCollection">The collection of all store items based on which the script should be created</param> /// <returns> /// A DDL script which creates schema objects based on contents of storeItemCollection /// and targeted for the version of the backend corresponding to the providerManifestToken. /// </returns> public string CreateDatabaseScript(string providerManifestToken, StoreItemCollection storeItemCollection) { return DbCreateDatabaseScript(providerManifestToken, storeItemCollection); } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Db")] protected virtual string DbCreateDatabaseScript(string providerManifestToken, StoreItemCollection storeItemCollection) { throw EntityUtil.ProviderIncompatible(Strings.ProviderDoesNotSupportCreateDatabaseScript); } /// <summary> /// Creates a database indicated by connection and creates schema objects /// (tables, primary keys, foreign keys) based on the contents of storeItemCollection. /// </summary> /// <param name="connection">Connection to a non-existent database that needs to be created /// and be populated with the store objects indicated by the storeItemCollection</param> /// <param name="commandTimeout">Execution timeout for any commands needed to create the database.</param> /// <param name="storeItemCollection">The collection of all store items based on which the script should be created<</param> public void CreateDatabase(DbConnection connection, int? commandTimeout, StoreItemCollection storeItemCollection) { DbCreateDatabase(connection, commandTimeout, storeItemCollection); } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Db")] protected virtual void DbCreateDatabase(DbConnection connection, int? commandTimeout, StoreItemCollection storeItemCollection) { throw EntityUtil.ProviderIncompatible(Strings.ProviderDoesNotSupportCreateDatabase); } /// <summary> /// Returns a value indicating whether given database exists on the server /// and/or whether schema objects contained in teh storeItemCollection have been created. /// If the provider can deduct the database only based on the connection, they do not need /// to additionally verify all elements of the storeItemCollection. /// </summary> /// <param name="connection">Connection to a database whose existence is checked by this method</param> /// <param name="commandTimeout">Execution timeout for any commands needed to determine the existence of the database</param> /// <param name="storeItemCollection">The collection of all store items contained in the database /// whose existence is determined by this method<</param> /// <returns>Whether the database indicated by the connection and the storeItemCollection exist</returns> public bool DatabaseExists(DbConnection connection, int? commandTimeout, StoreItemCollection storeItemCollection) { return DbDatabaseExists(connection, commandTimeout, storeItemCollection); } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Db")] protected virtual bool DbDatabaseExists(DbConnection connection, int? commandTimeout, StoreItemCollection storeItemCollection) { throw EntityUtil.ProviderIncompatible(Strings.ProviderDoesNotSupportDatabaseExists); } /// <summary> /// Deletes all store objects specified in the store item collection from the database and the database itself. /// </summary> /// <param name="connection">Connection to an existing database that needs to be deleted</param> /// <param name="commandTimeout">Execution timeout for any commands needed to delete the database</param> /// <param name="storeItemCollection">The collection of all store items contained in the database that should be deleted<</param> public void DeleteDatabase(DbConnection connection, int? commandTimeout, StoreItemCollection storeItemCollection) { DbDeleteDatabase(connection, commandTimeout, storeItemCollection); } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Db")] protected virtual void DbDeleteDatabase(DbConnection connection, int? commandTimeout, StoreItemCollection storeItemCollection) { throw EntityUtil.ProviderIncompatible(Strings.ProviderDoesNotSupportDeleteDatabase); } } }