/mcs/class/referencesource/System.Data.Linq/SqlClient/SqlProvider.cs
C# | 2024 lines | 1692 code | 204 blank | 128 comment | 393 complexity | 925ac6166544d3fb2119787e9cbcdd2f MD5 | raw file
Possible License(s): GPL-2.0, CC-BY-SA-3.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, LGPL-2.1, Unlicense, Apache-2.0
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Collections.ObjectModel;
- using System.ComponentModel;
- using System.Data;
- using System.Data.Common;
- using System.Data.Linq;
- using System.Data.Linq.Mapping;
- using System.Data.Linq.Provider;
- using System.Data.SqlClient;
- using System.Diagnostics;
- using System.IO;
- using System.Linq;
- using System.Linq.Expressions;
- using System.Reflection;
- using System.Text;
- using System.Globalization;
- using System.Diagnostics.CodeAnalysis;
- using Me = System.Data.Linq.SqlClient;
- using System.Runtime.Versioning;
- using System.Runtime.CompilerServices;
- namespace System.Data.Linq.SqlClient {
- public sealed class Sql2000Provider : SqlProvider {
- public Sql2000Provider()
- : base(ProviderMode.Sql2000) {
- }
- }
- public sealed class Sql2005Provider : SqlProvider {
- public Sql2005Provider()
- : base(ProviderMode.Sql2005) {
- }
- }
- public sealed class Sql2008Provider : SqlProvider {
- public Sql2008Provider()
- : base(ProviderMode.Sql2008) {
- }
- }
- [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification="Unknown reason.")]
- public class SqlProvider : IReaderProvider, IConnectionUser {
- private IDataServices services;
- private SqlConnectionManager conManager;
- private TypeSystemProvider typeProvider;
- private SqlFactory sqlFactory;
- private Translator translator;
- private IObjectReaderCompiler readerCompiler;
- private bool disposed;
- private int commandTimeout;
- private TextWriter log;
- string dbName = string.Empty;
- // stats and flags
- private int queryCount;
- private bool checkQueries;
- private OptimizationFlags optimizationFlags = OptimizationFlags.All;
- private bool enableCacheLookup = true;
- private ProviderMode mode = ProviderMode.NotYetDecided;
- private bool deleted = false;
- #if PERFORMANCE_BUILD
- private bool collectPerfInfo;
- private bool collectPerfInfoInitialized = false;
- private bool collectQueryPerf;
- internal bool CollectPerfInfo {
- get {
- if (!collectPerfInfoInitialized) {
- string s = System.Environment.GetEnvironmentVariable("CollectDLinqPerfInfo");
- collectPerfInfo = (s != null) && (s == "On");
- collectPerfInfoInitialized = true;
- }
- return this.collectPerfInfo;
- }
- }
- internal bool CollectQueryPerf {
- get { return this.collectQueryPerf; }
- }
- #endif
- internal enum ProviderMode {
- NotYetDecided,
- Sql2000,
- Sql2005,
- Sql2008,
- SqlCE
- }
- const string SqlCeProviderInvariantName = "System.Data.SqlServerCe.3.5";
- const string SqlCeDataReaderTypeName = "System.Data.SqlServerCe.SqlCeDataReader";
- const string SqlCeConnectionTypeName = "System.Data.SqlServerCe.SqlCeConnection";
- const string SqlCeTransactionTypeName = "System.Data.SqlServerCe.SqlCeTransaction";
- internal ProviderMode Mode {
- get {
- this.CheckDispose();
- this.CheckInitialized();
- this.InitializeProviderMode();
- return this.mode;
- }
- }
-
- private void InitializeProviderMode() {
- if (this.mode == ProviderMode.NotYetDecided) {
- if (this.IsSqlCe) {
- this.mode = ProviderMode.SqlCE;
- } else if (this.IsServer2KOrEarlier) {
- this.mode = ProviderMode.Sql2000;
- }
- else if (this.IsServer2005) {
- this.mode = ProviderMode.Sql2005;
- } else {
- this.mode = ProviderMode.Sql2008;
- }
- }
- if (this.typeProvider == null) {
- switch (this.mode) {
- case ProviderMode.Sql2000:
- this.typeProvider = SqlTypeSystem.Create2000Provider();
- break;
- case ProviderMode.Sql2005:
- this.typeProvider = SqlTypeSystem.Create2005Provider();
- break;
- case ProviderMode.Sql2008:
- this.typeProvider = SqlTypeSystem.Create2008Provider();
- break;
- case ProviderMode.SqlCE:
- this.typeProvider = SqlTypeSystem.CreateCEProvider();
- break;
- default:
- System.Diagnostics.Debug.Assert(false);
- break;
- }
- }
- if (this.sqlFactory == null) {
- this.sqlFactory = new SqlFactory(this.typeProvider, this.services.Model);
- this.translator = new Translator(this.services, this.sqlFactory, this.typeProvider);
- }
- }
- /// <summary>
- /// Return true if the current connection is SQLCE.
- /// </summary>
- private bool IsSqlCe {
- get {
- DbConnection con = conManager.UseConnection(this);
- try {
- if (String.CompareOrdinal(con.GetType().FullName, SqlCeConnectionTypeName) == 0) {
- return true;
- }
- } finally {
- conManager.ReleaseConnection(this);
- }
- return false;
- }
- }
-
- /// <summary>
- /// Return true if this is a 2K (or earlier) server. This may be a round trip to the server.
- /// </summary>
- private bool IsServer2KOrEarlier {
- get {
- DbConnection con = conManager.UseConnection(this);
- try {
- string serverVersion = con.ServerVersion;
- if (serverVersion.StartsWith("06.00.", StringComparison.Ordinal)) {
- return true;
- }
- else if (serverVersion.StartsWith("06.50.", StringComparison.Ordinal)) {
- return true;
- }
- else if (serverVersion.StartsWith("07.00.", StringComparison.Ordinal)) {
- return true;
- }
- else if (serverVersion.StartsWith("08.00.", StringComparison.Ordinal)) {
- return true;
- }
- return false;
- }
- finally {
- conManager.ReleaseConnection(this);
- }
- }
- }
- /// <summary>
- /// Return true if this is a SQL 2005 server. This may be a round trip to the server.
- /// </summary>
- private bool IsServer2005 {
- get {
- DbConnection con = conManager.UseConnection(this);
- try {
- string serverVersion = con.ServerVersion;
- if (serverVersion.StartsWith("09.00.", StringComparison.Ordinal)) {
- return true;
- }
- return false;
- }
- finally {
- conManager.ReleaseConnection(this);
- }
- }
- }
- DbConnection IProvider.Connection {
- get {
- this.CheckDispose();
- this.CheckInitialized();
- return this.conManager.Connection;
- }
- }
- TextWriter IProvider.Log {
- get {
- this.CheckDispose();
- this.CheckInitialized();
- return this.log;
- }
- set {
- this.CheckDispose();
- this.CheckInitialized();
- this.log = value;
- }
- }
- DbTransaction IProvider.Transaction {
- get {
- this.CheckDispose();
- this.CheckInitialized();
- return this.conManager.Transaction;
- }
- set {
- this.CheckDispose();
- this.CheckInitialized();
- this.conManager.Transaction = value;
- }
- }
- int IProvider.CommandTimeout {
- get {
- this.CheckDispose();
- return this.commandTimeout;
- }
- set {
- this.CheckDispose();
- this.commandTimeout = value;
- }
- }
- /// <summary>
- /// Expose a test hook which controls which SQL optimizations are executed.
- /// </summary>
- internal OptimizationFlags OptimizationFlags {
- get {
- CheckDispose();
- return this.optimizationFlags;
- }
- set {
- CheckDispose();
- this.optimizationFlags = value;
- }
- }
- /// <summary>
- /// Validate queries as they are generated.
- /// </summary>
- internal bool CheckQueries {
- get {
- CheckDispose();
- return checkQueries;
- }
- set {
- CheckDispose();
- checkQueries = value;
- }
- }
- internal bool EnableCacheLookup {
- get {
- CheckDispose();
- return this.enableCacheLookup;
- }
- set {
- CheckDispose();
- this.enableCacheLookup = value;
- }
- }
- internal int QueryCount {
- get {
- CheckDispose();
- return this.queryCount;
- }
- }
- internal int MaxUsers {
- get {
- CheckDispose();
- return this.conManager.MaxUsers;
- }
- }
- IDataServices IReaderProvider.Services {
- get { return this.services; }
- }
- IConnectionManager IReaderProvider.ConnectionManager {
- get { return this.conManager; }
- }
- public SqlProvider() {
- this.mode = ProviderMode.NotYetDecided;
- }
- internal SqlProvider(ProviderMode mode) {
- this.mode = mode;
- }
- private void CheckInitialized() {
- if (this.services == null) {
- throw Error.ContextNotInitialized();
- }
- }
- private void CheckNotDeleted() {
- if (this.deleted) {
- throw Error.DatabaseDeleteThroughContext();
- }
- }
- [ResourceExposure(ResourceScope.Machine)] // connection parameter may refer to filenames.
- void IProvider.Initialize(IDataServices dataServices, object connection) {
- if (dataServices == null) {
- throw Error.ArgumentNull("dataServices");
- }
- this.services = dataServices;
- DbConnection con;
- DbTransaction tx = null;
- string fileOrServerOrConnectionString = connection as string;
- if (fileOrServerOrConnectionString != null) {
- string connectionString = this.GetConnectionString(fileOrServerOrConnectionString);
- this.dbName = this.GetDatabaseName(connectionString);
- if (this.dbName.EndsWith(".sdf", StringComparison.OrdinalIgnoreCase)) {
- this.mode = ProviderMode.SqlCE;
- }
- if (this.mode == ProviderMode.SqlCE) {
- DbProviderFactory factory = SqlProvider.GetProvider(SqlCeProviderInvariantName);
- if (factory == null) {
- throw Error.ProviderNotInstalled(this.dbName, SqlCeProviderInvariantName);
- }
- con = factory.CreateConnection();
- }
- else {
- con = new SqlConnection();
- }
- con.ConnectionString = connectionString;
- }
- else {
- // We only support SqlTransaction and SqlCeTransaction
- tx = connection as SqlTransaction;
- if (tx == null) {
- // See if it's a SqlCeTransaction
- if (connection.GetType().FullName == SqlCeTransactionTypeName) {
- tx = connection as DbTransaction;
- }
- }
- if (tx != null) {
- connection = tx.Connection;
- }
- con = connection as DbConnection;
- if (con == null) {
- throw Error.InvalidConnectionArgument("connection");
- }
- if (con.GetType().FullName == SqlCeConnectionTypeName) {
- this.mode = ProviderMode.SqlCE;
- }
- this.dbName = this.GetDatabaseName(con.ConnectionString);
- }
-
- // initialize to the default command timeout
- using (DbCommand c = con.CreateCommand()) {
- this.commandTimeout = c.CommandTimeout;
- }
- int maxUsersPerConnection = 1;
- if (con.ConnectionString.IndexOf("MultipleActiveResultSets", StringComparison.OrdinalIgnoreCase) >= 0) {
- DbConnectionStringBuilder builder = new DbConnectionStringBuilder();
- builder.ConnectionString = con.ConnectionString;
- if (string.Compare((string)builder["MultipleActiveResultSets"], "true", StringComparison.OrdinalIgnoreCase) == 0) {
- maxUsersPerConnection = 10;
- }
- }
- // If fileOrServerOrConnectionString != null, that means we just created the connection instance and we have to tell
- // the SqlConnectionManager that it should dispose the connection when the context is disposed. Otherwise the user owns
- // the connection and should dispose of it themselves.
- this.conManager = new SqlConnectionManager(this, con, maxUsersPerConnection, fileOrServerOrConnectionString != null /*disposeConnection*/);
- if (tx != null) {
- this.conManager.Transaction = tx;
- }
- #if DEBUG
- SqlNode.Formatter = new SqlFormatter();
- #endif
- #if ILGEN
- Type readerType;
- if (this.mode == ProviderMode.SqlCE) {
- readerType = con.GetType().Module.GetType(SqlCeDataReaderTypeName);
- }
- else if (con is SqlConnection) {
- readerType = typeof(SqlDataReader);
- }
- else {
- readerType = typeof(DbDataReader);
- }
- this.readerCompiler = new ObjectReaderCompiler(readerType, this.services);
- #else
- this.readerCompiler = new ObjectReaderBuilder(this, this.services);
- #endif
- }
- private static DbProviderFactory GetProvider(string providerName) {
- bool hasProvider =
- DbProviderFactories.GetFactoryClasses().Rows.OfType<DataRow>()
- .Select(r => (string)r["InvariantName"])
- .Contains(providerName, StringComparer.OrdinalIgnoreCase);
- if (hasProvider) {
- return DbProviderFactories.GetFactory(providerName);
- }
- return null;
- }
- #region Dispose\Finalize
- public void Dispose() {
- this.disposed = true;
- Dispose(true);
- // Technically, calling GC.SuppressFinalize is not required because the class does not
- // have a finalizer, but it does no harm, protects against the case where a finalizer is added
- // in the future, and prevents an FxCop warning.
- GC.SuppressFinalize(this);
- }
- // Not implementing finalizer here because there are no unmanaged resources
- // to release. See http://msdnwiki.microsoft.com/en-us/mtpswiki/12afb1ea-3a17-4a3f-a1f0-fcdb853e2359.aspx
- // The bulk of the clean-up code is implemented in Dispose(bool)
- protected virtual void Dispose(bool disposing) {
- // Implemented but empty so that derived contexts can implement
- // a finalizer that potentially cleans up unmanaged resources.
- if (disposing) {
- this.services = null;
- if (this.conManager != null) {
- this.conManager.DisposeConnection();
- }
- this.conManager = null;
- this.typeProvider = null;
- this.sqlFactory = null;
- this.translator = null;
- this.readerCompiler = null;
- this.log = null;
- }
- }
- internal void CheckDispose() {
- if (this.disposed) {
- throw Error.ProviderCannotBeUsedAfterDispose();
- }
- }
- #endregion
- private string GetConnectionString(string fileOrServerOrConnectionString) {
- if (fileOrServerOrConnectionString.IndexOf('=') >= 0) {
- return fileOrServerOrConnectionString;
- }
- else {
- DbConnectionStringBuilder builder = new DbConnectionStringBuilder();
- if (fileOrServerOrConnectionString.EndsWith(".mdf", StringComparison.OrdinalIgnoreCase)) {
- // if just a database file is specified, default to local SqlExpress instance
- builder.Add("AttachDBFileName", fileOrServerOrConnectionString);
- builder.Add("Server", "localhost\\sqlexpress");
- builder.Add("Integrated Security", "SSPI");
- builder.Add("User Instance", "true");
- builder.Add("MultipleActiveResultSets", "true");
- }
- else if (fileOrServerOrConnectionString.EndsWith(".sdf", StringComparison.OrdinalIgnoreCase)) {
- // A SqlCE database file has been specified
- builder.Add("Data Source", fileOrServerOrConnectionString);
- }
- else {
- builder.Add("Server", fileOrServerOrConnectionString);
- builder.Add("Database", this.services.Model.DatabaseName);
- builder.Add("Integrated Security", "SSPI");
- }
- return builder.ToString();
- }
- }
- private string GetDatabaseName(string constr) {
- DbConnectionStringBuilder builder = new DbConnectionStringBuilder();
- builder.ConnectionString = constr;
- if (builder.ContainsKey("Initial Catalog")) {
- return (string)builder["Initial Catalog"];
- }
- else if (builder.ContainsKey("Database")) {
- return (string)builder["Database"];
- }
- else if (builder.ContainsKey("AttachDBFileName")) {
- return (string)builder["AttachDBFileName"];
- }
- else if (builder.ContainsKey("Data Source")
- && ((string)builder["Data Source"]).EndsWith(".sdf", StringComparison.OrdinalIgnoreCase)) {
- return (string)builder["Data Source"];
- }
- else {
- return this.services.Model.DatabaseName;
- }
- }
- [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "These issues are related to our use of if-then and case statements for node types, which adds to the complexity count however when reviewed they are easy to navigate and understand.")]
- [ResourceExposure(ResourceScope.None)] // Exposure is via other methods that set dbName.
- [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] // File.Exists method call.
- void IProvider.CreateDatabase() {
- this.CheckDispose();
- this.CheckInitialized();
- // Don't need to call CheckNotDeleted() here since we allow CreateDatabase after DeleteDatabase
- // Don't need to call InitializeProviderMode() here since we don't need to know the provider to do this.
- string catalog = null;
- string filename = null;
- DbConnectionStringBuilder builder = new DbConnectionStringBuilder();
- builder.ConnectionString = this.conManager.Connection.ConnectionString;
- if (this.conManager.Connection.State == ConnectionState.Closed) {
- if (this.mode == ProviderMode.SqlCE) {
- if (!File.Exists(this.dbName)) {
- Type engineType = this.conManager.Connection.GetType().Module.GetType("System.Data.SqlServerCe.SqlCeEngine");
- object engine = Activator.CreateInstance(engineType, new object[] { builder.ToString() });
- try {
- engineType.InvokeMember("CreateDatabase", BindingFlags.Instance | BindingFlags.Public | BindingFlags.InvokeMethod, null, engine, new object[] { }, CultureInfo.InvariantCulture);
- }
- catch (TargetInvocationException tie) {
- throw tie.InnerException;
- }
- finally {
- IDisposable disp = engine as IDisposable;
- if (disp != null) {
- disp.Dispose();
- }
- }
- }
- else {
- throw Error.CreateDatabaseFailedBecauseSqlCEDatabaseAlreadyExists(this.dbName);
- }
- }
- else {
- // get connection string w/o reference to new catalog
- object val;
- if (builder.TryGetValue("Initial Catalog", out val)) {
- catalog = val.ToString();
- builder.Remove("Initial Catalog");
- }
- if (builder.TryGetValue("Database", out val)) {
- catalog = val.ToString();
- builder.Remove("Database");
- }
- if (builder.TryGetValue("AttachDBFileName", out val)) {
- filename = val.ToString();
- builder.Remove("AttachDBFileName");
- }
- }
- this.conManager.Connection.ConnectionString = builder.ToString();
- }
- else {
- if (this.mode == ProviderMode.SqlCE) {
- if (File.Exists(this.dbName)) {
- throw Error.CreateDatabaseFailedBecauseSqlCEDatabaseAlreadyExists(this.dbName);
- }
- }
- object val;
- if (builder.TryGetValue("Initial Catalog", out val)) {
- catalog = val.ToString();
- }
- if (builder.TryGetValue("Database", out val)) {
- catalog = val.ToString();
- }
- if (builder.TryGetValue("AttachDBFileName", out val)) {
- filename = val.ToString();
- }
- }
- if (String.IsNullOrEmpty(catalog)) {
- if (!String.IsNullOrEmpty(filename)) {
- catalog = Path.GetFullPath(filename);
- }
- else if (!String.IsNullOrEmpty(this.dbName)) {
- catalog = this.dbName;
- }
- else {
- throw Error.CouldNotDetermineCatalogName();
- }
- }
- this.conManager.UseConnection(this);
- this.conManager.AutoClose = false;
- try {
- if (this.services.Model.GetTables().FirstOrDefault() == null) {
- // we have no tables to create
- throw Error.CreateDatabaseFailedBecauseOfContextWithNoTables(this.services.Model.DatabaseName);
- }
- this.deleted = false;
- // create database
- if (this.mode == ProviderMode.SqlCE) {
- // create tables
- foreach (MetaTable table in this.services.Model.GetTables()) {
- string command = SqlBuilder.GetCreateTableCommand(table);
- if (!String.IsNullOrEmpty(command)) {
- this.ExecuteCommand(command);
- }
- }
- // create all foreign keys after all tables are defined
- foreach (MetaTable table in this.services.Model.GetTables()) {
- foreach (string command in SqlBuilder.GetCreateForeignKeyCommands(table)) {
- if (!String.IsNullOrEmpty(command)) {
- this.ExecuteCommand(command);
- }
- }
- }
- }
- else {
- string createdb = SqlBuilder.GetCreateDatabaseCommand(catalog, filename, Path.ChangeExtension(filename, ".ldf"));
- this.ExecuteCommand(createdb);
- this.conManager.Connection.ChangeDatabase(catalog);
- // create the schemas that our tables will need
- // cannot be batched together with the rest of the CREATE TABLES
- if (this.mode == ProviderMode.Sql2005 || this.mode == ProviderMode.Sql2008) {
- HashSet<string> schemaCommands = new HashSet<string>();
- foreach (MetaTable table in this.services.Model.GetTables()) {
- string schemaCommand = SqlBuilder.GetCreateSchemaForTableCommand(table);
- if (!string.IsNullOrEmpty(schemaCommand)) {
- schemaCommands.Add(schemaCommand);
- }
- }
- foreach (string schemaCommand in schemaCommands) {
- this.ExecuteCommand(schemaCommand);
- }
- }
- StringBuilder sb = new StringBuilder();
- // create tables
- foreach (MetaTable table in this.services.Model.GetTables()) {
- string createTable = SqlBuilder.GetCreateTableCommand(table);
- if (!string.IsNullOrEmpty(createTable)) {
- sb.AppendLine(createTable);
- }
- }
- // create all foreign keys after all tables are defined
- foreach (MetaTable table in this.services.Model.GetTables()) {
- foreach (string createFK in SqlBuilder.GetCreateForeignKeyCommands(table)) {
- if (!string.IsNullOrEmpty(createFK)) {
- sb.AppendLine(createFK);
- }
- }
- }
- if (sb.Length > 0) {
- // must be on when creating indexes on computed columns
- sb.Insert(0, "SET ARITHABORT ON" + Environment.NewLine);
- this.ExecuteCommand(sb.ToString());
- }
- }
- }
- finally {
- this.conManager.ReleaseConnection(this);
- if (this.conManager.Connection is SqlConnection) {
- SqlConnection.ClearAllPools();
- }
- }
- }
- [ResourceExposure(ResourceScope.None)] // Exposure is via other methods that set dbName.
- [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] // File.Delete method call.
- void IProvider.DeleteDatabase() {
- this.CheckDispose();
- this.CheckInitialized();
- // Don't need to call InitializeProviderMode() here since we don't need to know the provider to do this.
- if (this.deleted) {
- // 2nd delete is no-op.
- return;
- }
- if (this.mode == ProviderMode.SqlCE) {
- ((IProvider)this).ClearConnection();
- System.Diagnostics.Debug.Assert(this.conManager.Connection.State == ConnectionState.Closed);
- File.Delete(this.dbName);
- this.deleted = true;
- }
- else {
- string holdConnStr = conManager.Connection.ConnectionString;
- DbConnection con = this.conManager.UseConnection(this);
- try {
- con.ChangeDatabase("master");
- if (con is SqlConnection) {
- SqlConnection.ClearAllPools();
- }
- if (this.log != null) {
- this.log.WriteLine(Strings.LogAttemptingToDeleteDatabase(this.dbName));
- }
- this.ExecuteCommand(SqlBuilder.GetDropDatabaseCommand(this.dbName));
- this.deleted = true;
- }
- finally {
- this.conManager.ReleaseConnection(this);
- if (conManager.Connection.State == ConnectionState.Closed &&
- string.Compare(conManager.Connection.ConnectionString, holdConnStr, StringComparison.Ordinal) != 0) {
- // Credential information may have been stripped from the connection
- // string as a result of opening the connection. Restore the full
- // connection string.
- conManager.Connection.ConnectionString = holdConnStr;
- }
- }
- }
- }
- [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification="Microsoft: Code needs to return false regarless of exception.")]
- [ResourceExposure(ResourceScope.None)] // Exposure is via other methods that set dbName.
- [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] // File.Exists method call.
- bool IProvider.DatabaseExists() {
- this.CheckDispose();
- this.CheckInitialized();
- if (this.deleted) {
- return false;
- }
- // Don't need to call InitializeProviderMode() here since we don't need to know the provider to do this.
- bool exists = false;
- if (this.mode == ProviderMode.SqlCE) {
- exists = File.Exists(this.dbName);
- }
- else {
- string holdConnStr = conManager.Connection.ConnectionString;
- try {
- // If no database name is explicitly specified on the connection,
- // UseConnection will connect to 'Master', which is why after connecting
- // we call ChangeDatabase to verify that the database actually exists.
- this.conManager.UseConnection(this);
- this.conManager.Connection.ChangeDatabase(this.dbName);
- this.conManager.ReleaseConnection(this);
- exists = true;
- } catch (Exception) {
- } finally {
- if (conManager.Connection.State == ConnectionState.Closed &&
- string.Compare(conManager.Connection.ConnectionString, holdConnStr, StringComparison.Ordinal) != 0) {
- // Credential information may have been stripped from the connection
- // string as a result of opening the connection. Restore the full
- // connection string.
- conManager.Connection.ConnectionString = holdConnStr;
- }
- }
- }
- return exists;
- }
- void IConnectionUser.CompleteUse() {
- }
- void IProvider.ClearConnection() {
- this.CheckDispose();
- this.CheckInitialized();
- this.conManager.ClearConnection();
- }
- private void ExecuteCommand(string command) {
- if (this.log != null) {
- this.log.WriteLine(command);
- this.log.WriteLine();
- }
- IDbCommand cmd = this.conManager.Connection.CreateCommand();
- cmd.CommandTimeout = this.commandTimeout;
- cmd.Transaction = this.conManager.Transaction;
- cmd.CommandText = command;
- cmd.ExecuteNonQuery();
- }
- ICompiledQuery IProvider.Compile(Expression query) {
- this.CheckDispose();
- this.CheckInitialized();
- if (query == null) {
- throw Error.ArgumentNull("query");
- }
- this.InitializeProviderMode();
- SqlNodeAnnotations annotations = new SqlNodeAnnotations();
- QueryInfo[] qis = this.BuildQuery(query, annotations);
- CheckSqlCompatibility(qis, annotations);
-
- LambdaExpression lambda = query as LambdaExpression;
- if (lambda != null) {
- query = lambda.Body;
- }
- IObjectReaderFactory factory = null;
- ICompiledSubQuery[] subQueries = null;
- QueryInfo qi = qis[qis.Length - 1];
- if (qi.ResultShape == ResultShape.Singleton) {
- subQueries = this.CompileSubQueries(qi.Query);
- factory = this.GetReaderFactory(qi.Query, qi.ResultType);
- }
- else if (qi.ResultShape == ResultShape.Sequence) {
- subQueries = this.CompileSubQueries(qi.Query);
- factory = this.GetReaderFactory(qi.Query, TypeSystem.GetElementType(qi.ResultType));
- }
- return new CompiledQuery(this, query, qis, factory, subQueries);
- }
- private ICompiledSubQuery CompileSubQuery(SqlNode query, Type elementType, ReadOnlyCollection<Me.SqlParameter> parameters) {
- query = SqlDuplicator.Copy(query);
- SqlNodeAnnotations annotations = new SqlNodeAnnotations();
- QueryInfo[] qis = this.BuildQuery(ResultShape.Sequence, TypeSystem.GetSequenceType(elementType), query, parameters, annotations);
- System.Diagnostics.Debug.Assert(qis.Length == 1);
- QueryInfo qi = qis[0];
- ICompiledSubQuery[] subQueries = this.CompileSubQueries(qi.Query);
- IObjectReaderFactory factory = this.GetReaderFactory(qi.Query, elementType);
- CheckSqlCompatibility(qis, annotations);
- return new CompiledSubQuery(qi, factory, parameters, subQueries);
- }
- IExecuteResult IProvider.Execute(Expression query) {
- this.CheckDispose();
- this.CheckInitialized();
- this.CheckNotDeleted();
- if (query == null) {
- throw Error.ArgumentNull("query");
- }
- this.InitializeProviderMode();
- #if PERFORMANCE_BUILD
- PerformanceCounter pcBuildQuery = null, bpcBuildQuery = null, pcExecQuery = null, bpcExecQuery = null,
- pcSession = null, bpcSession = null;
- PerfTimer timerAll = null, timer = null;
- if (this.CollectPerfInfo) {
- string s = System.Environment.GetEnvironmentVariable("EnableDLinqQueryPerf");
- collectQueryPerf = (s != null && s == "On");
- }
- if (collectQueryPerf) {
- pcBuildQuery = new PerformanceCounter("DLinq", "BuildQueryElapsedTime", false);
- bpcBuildQuery = new PerformanceCounter("DLinq", "BuildQueryElapsedTimeBase", false);
- pcExecQuery = new PerformanceCounter("DLinq", "ExecuteQueryElapsedTime", false);
- bpcExecQuery = new PerformanceCounter("DLinq", "ExecuteQueryElapsedTimeBase", false);
- pcSession = new PerformanceCounter("DLinq", "SessionExecuteQueryElapsedTime", false);
- bpcSession = new PerformanceCounter("DLinq", "SessionExecuteQueryElapsedTimeBase", false);
- timerAll = new PerfTimer();
- timer = new PerfTimer();
- timerAll.Start();
- }
- #endif
- query = Funcletizer.Funcletize(query);
- if (this.EnableCacheLookup) {
- IExecuteResult cached = this.GetCachedResult(query);
- if (cached != null) {
- return cached;
- }
- }
- #if PERFORMANCE_BUILD
- if (collectQueryPerf) {
- timer.Start();
- }
- #endif
- SqlNodeAnnotations annotations = new SqlNodeAnnotations();
- QueryInfo[] qis = this.BuildQuery(query, annotations);
- CheckSqlCompatibility(qis, annotations);
- LambdaExpression lambda = query as LambdaExpression;
- if (lambda != null) {
- query = lambda.Body;
- }
- IObjectReaderFactory factory = null;
- ICompiledSubQuery[] subQueries = null;
- QueryInfo qi = qis[qis.Length - 1];
- if (qi.ResultShape == ResultShape.Singleton) {
- subQueries = this.CompileSubQueries(qi.Query);
- factory = this.GetReaderFactory(qi.Query, qi.ResultType);
- }
- else if (qi.ResultShape == ResultShape.Sequence) {
- subQueries = this.CompileSubQueries(qi.Query);
- factory = this.GetReaderFactory(qi.Query, TypeSystem.GetElementType(qi.ResultType));
- }
- #if PERFORMANCE_BUILD
- if (collectQueryPerf) {
- timer.Stop();
- pcBuildQuery.IncrementBy(timer.Duration);
- bpcBuildQuery.Increment();
- }
- #endif
- #if PERFORMANCE_BUILD
- if (collectQueryPerf) {
- timer.Start();
- }
- #endif
- IExecuteResult result = this.ExecuteAll(query, qis, factory, null, subQueries);
- #if PERFORMANCE_BUILD
- if (collectQueryPerf) {
- timer.Stop();
- pcSession.IncrementBy(timer.Duration);
- bpcSession.Increment();
- timerAll.Stop();
- pcExecQuery.IncrementBy(timerAll.Duration);
- bpcExecQuery.Increment();
- }
- #endif
- return result;
- }
- private ICompiledSubQuery[] CompileSubQueries(SqlNode query) {
- return new SubQueryCompiler(this).Compile(query);
- }
- class SubQueryCompiler : SqlVisitor {
- SqlProvider provider;
- List<ICompiledSubQuery> subQueries;
- internal SubQueryCompiler(SqlProvider provider) {
- this.provider = provider;
- }
- internal ICompiledSubQuery[] Compile(SqlNode node) {
- this.subQueries = new List<ICompiledSubQuery>();
- this.Visit(node);
- return this.subQueries.ToArray();
- }
- internal override SqlSelect VisitSelect(SqlSelect select) {
- this.Visit(select.Selection);
- return select;
- }
- internal override SqlExpression VisitSubSelect(SqlSubSelect ss) {
- return ss;
- }
- internal override SqlExpression VisitClientQuery(SqlClientQuery cq) {
- Type clientElementType = cq.Query.NodeType == SqlNodeType.Multiset ? TypeSystem.GetElementType(cq.ClrType) : cq.ClrType;
- ICompiledSubQuery c = this.provider.CompileSubQuery(cq.Query.Select, clientElementType, cq.Parameters.AsReadOnly());
- cq.Ordinal = this.subQueries.Count;
- this.subQueries.Add(c);
- return cq;
- }
- }
- /// <summary>
- /// Look for compatibility annotations for the set of providers we
- /// add annotations for.
- /// </summary>
- private void CheckSqlCompatibility(QueryInfo[] queries, SqlNodeAnnotations annotations) {
- if (this.Mode == ProviderMode.Sql2000 ||
- this.Mode == ProviderMode.SqlCE) {
- for (int i = 0, n = queries.Length; i < n; i++) {
- SqlServerCompatibilityCheck.ThrowIfUnsupported(queries[i].Query, annotations, this.Mode);
- }
- }
- }
- private IExecuteResult ExecuteAll(Expression query, QueryInfo[] queryInfos, IObjectReaderFactory factory, object[] userArguments, ICompiledSubQuery[] subQueries) {
- IExecuteResult result = null;
- object lastResult = null;
- for (int i = 0, n = queryInfos.Length; i < n; i++) {
- if (i < n - 1) {
- result = this.Execute(query, queryInfos[i], null, null, userArguments, subQueries, lastResult);
- }
- else {
- result = this.Execute(query, queryInfos[i], factory, null, userArguments, subQueries, lastResult);
- }
- if (queryInfos[i].ResultShape == ResultShape.Return) {
- lastResult = result.ReturnValue;
- }
- }
- return result;
- }
- [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
- private IExecuteResult GetCachedResult(Expression query) {
- object obj = this.services.GetCachedObject(query);
- if (obj != null) {
- switch (this.GetResultShape(query)) {
- case ResultShape.Singleton:
- return new ExecuteResult(null, null, null, obj);
- case ResultShape.Sequence:
- return new ExecuteResult(null, null, null,
- Activator.CreateInstance(
- typeof(SequenceOfOne<>).MakeGenericType(TypeSystem.GetElementType(this.GetResultType(query))),
- BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { obj }, null
- ));
- }
- }
- return null;
- }
- [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification="Unknown reason.")]
- [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
- private IExecuteResult Execute(Expression query, QueryInfo queryInfo, IObjectReaderFactory factory, object[] parentArgs, object[] userArgs, ICompiledSubQuery[] subQueries, object lastResult) {
- this.InitializeProviderMode();
- DbConnection con = this.conManager.UseConnection(this);
- try {
- DbCommand cmd = con.CreateCommand();
- cmd.CommandText = queryInfo.CommandText;
- cmd.Transaction = this.conManager.Transaction;
- cmd.CommandTimeout = this.commandTimeout;
- AssignParameters(cmd, queryInfo.Parameters, userArgs, lastResult);
- LogCommand(this.log, cmd);
- this.queryCount += 1;
- switch (queryInfo.ResultShape) {
- default:
- case ResultShape.Return: {
- return new ExecuteResult(cmd, queryInfo.Parameters, null, cmd.ExecuteNonQuery(), true);
- }
- case ResultShape.Singleton: {
- DbDataReader reader = cmd.ExecuteReader();
- IObjectReader objReader = factory.Create(reader, true, this, parentArgs, userArgs, subQueries);
- this.conManager.UseConnection(objReader.Session);
- try {
- IEnumerable sequence = (IEnumerable)Activator.CreateInstance(
- typeof(OneTimeEnumerable<>).MakeGenericType(queryInfo.ResultType),
- BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null,
- new object[] { objReader }, null
- );
- object value = null;
- MethodCallExpression mce = query as MethodCallExpression;
- MethodInfo sequenceMethod = null;
- if (mce != null && (
- mce.Method.DeclaringType == typeof(Queryable) ||
- mce.Method.DeclaringType == typeof(Enumerable))
- ) {
- switch (mce.Method.Name) {
- case "First":
- case "FirstOrDefault":
- case "SingleOrDefault":
- sequenceMethod = TypeSystem.FindSequenceMethod(mce.Method.Name, sequence);
- break;
- case "Single":
- default:
- sequenceMethod = TypeSystem.FindSequenceMethod("Single", sequence);
- break;
- }
- }
- else {
- sequenceMethod = TypeSystem.FindSequenceMethod("SingleOrDefault", sequence);
- }
- // When dynamically invoking the sequence method, we want to
- // return the inner exception if the invocation fails
- if (sequenceMethod != null) {
- try {
- value = sequenceMethod.Invoke(null, new object[] { sequence });
- }
- catch (TargetInvocationException tie) {
- if (tie.InnerException != null) {
- throw tie.InnerException;
- }
- throw;
- }
- }
- return new ExecuteResult(cmd, queryInfo.Parameters, objReader.Session, value);
- }
- finally {
- objReader.Dispose();
- }
- }
- case ResultShape.Sequence: {
- DbDataReader reader = cmd.ExecuteReader();
- IObjectReader objReader = factory.Create(reader, true, this, parentArgs, userArgs, subQueries);
- this.conManager.UseConnection(objReader.Session);
- IEnumerable sequence = (IEnumerable)Activator.CreateInstance(
- typeof(OneTimeEnumerable<>).MakeGenericType(TypeSystem.GetElementType(queryInfo.ResultType)),
- BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null,
- new object[] { objReader }, null
- );
- if (typeof(IQueryable).IsAssignableFrom(queryInfo.ResultType)) {
- sequence = sequence.AsQueryable();
- }
- ExecuteResult result = new ExecuteResult(cmd, queryInfo.Parameters, objReader.Session);
- MetaFunction function = this.GetFunction(query);
- if (function != null && !function.IsComposable) {
- sequence = (IEnumerable)Activator.CreateInstance(
- typeof(SingleResult<>).MakeGenericType(TypeSystem.GetElementType(queryInfo.ResultType)),
- BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null,
- new object[] { sequence, result, this.services.Context }, null
- );
- }
- result.ReturnValue = sequence;
- return result;
- }
- case ResultShape.MultipleResults: {
- DbDataReader reader = cmd.ExecuteReader();
- IObjectReaderSession session = this.readerCompiler.CreateSession(reader, this, parentArgs, userArgs, subQueries);
- this.conManager.UseConnection(session);
- MetaFunction function = this.GetFunction(query);
- ExecuteResult result = new ExecuteResult(cmd, queryInfo.Parameters, session);
- result.ReturnValue = new MultipleResults(this, function, session, result);
- return result;
- }
- }
- }
- finally {
- this.conManager.ReleaseConnection(this);
- }
- }
- private MetaFunction GetFunction(Expression query) {
- LambdaExpression lambda = query as LambdaExpression;
- if (lambda != null) {
- query = lambda.Body;
- }
- MethodCallExpression mc = query as MethodCallExpression;
- if (mc != null && typeof(DataContext).IsAssignableFrom(mc.Method.DeclaringType)) {
- return this.services.Model.GetFunction(mc.Method);
- }
- return null;
- }
- private void LogCommand(TextWriter writer, DbCommand cmd) {
- if (writer != null) {
- writer.WriteLine(cmd.CommandText);
- foreach (DbParameter p in cmd.Parameters) {
- int prec = 0;
- int scale = 0;
- PropertyInfo piPrecision = p.GetType().GetProperty("Precision");
- if (piPrecision != null) {
- prec = (int)Convert.ChangeType(piPrecision.GetValue(p, null), typeof(int), CultureInfo.InvariantCulture);
- }
- PropertyInfo piScale = p.GetType().GetProperty("Scale");
- if (piScale != null) {
- scale = (int)Convert.ChangeType(piScale.GetValue(p, null), typeof(int), CultureInfo.InvariantCulture);
- }
- var sp = p as System.Data.SqlClient.SqlParameter;
- writer.WriteLine("-- {0}: {1} {2} (Size = {3}; Prec = {4}; Scale = {5}) [{6}]",
- p.ParameterName,
- p.Direction,
- sp == null ? p.DbType.ToString() : sp.SqlDbType.ToString(),
- p.Size.ToString(System.Globalization.CultureInfo.CurrentCulture),
- prec,
- scale,
- sp == null ? p.Value : sp.SqlValue);
- }
- writer.WriteLine("-- Context: {0}({1}) Model: {2} Build: {3}", this.GetType().Name, this.Mode, this.services.Model.GetType().Name, ThisAssembly.InformationalVersion);
- writer.WriteLine();
- }
- }
- private void AssignParameters(DbCommand cmd, ReadOnlyCollection<SqlParameterInfo> parms, object[] userArguments, object lastResult) {
- if (parms != null) {
- foreach (SqlParameterInfo pi in parms) {
- DbParameter p = cmd.CreateParameter();
- p.ParameterName = pi.Parameter.Name;
- p.Direction = pi.Parameter.Direction;
- if (pi.Parameter.Direction == ParameterDirection.Input ||
- pi.Parameter.Direction == ParameterDirection.InputOutput) {
- object value = pi.Value;
- switch (pi.Type) {
- case SqlParameterType.UserArgument:
- try {
- value = pi.Accessor.DynamicInvoke(new object[] { userArguments });
- } catch (System.Reflection.TargetInvocationException e) {
- throw e.InnerException;
- }
- break;
- case SqlParameterType.PreviousResult:
- value = lastResult;
- break;
- }
- this.typeProvider.InitializeParameter(pi.Parameter.SqlType, p, value);
- }
- else {
- this.typeProvider.InitializeParameter(pi.Parameter.SqlType, p, null);
- }
- cmd.Parameters.Add(p);
- }
- }
- }
- [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
- IEnumerable IProvider.Translate(Type elementType, DbDataReader reader) {
- this.CheckDispose();
- this.CheckInitialized();
- this.InitializeProviderMode();
- if (elementType == null) {
- throw Error.ArgumentNull("elementType");
- }
- if (reader == null) {
- throw Error.ArgumentNull("reader");
- }
- MetaType rowType = services.Model.GetMetaType(elementType);
- IObjectReaderFactory factory = this.GetDefaultFactory(rowType);
- IEnumerator e = factory.Create(reader, true, this, null, null, null);
- Type enumerableType = typeof(OneTimeEnumerable<>).MakeGenericType(elementType);
- return (IEnumerable)Activator.CreateInstance(enumerableType, BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { e }, null);
- }
- IMultipleResults IProvider.Translate(DbDataReader reader) {
- this.CheckDispose();
- this.CheckInitialized();
- this.InitializeProviderMode();
- if (reader == null) {
- throw Error.ArgumentNull("reader");
- }
- IObjectReaderSession session = this.readerCompiler.CreateSession(reader, this, null, null, null);
- return new MultipleResults(this, null, session, null);
- }
- string IProvider.GetQueryText(Expression query) {
- this.CheckDispose();
- this.CheckInitialized();
- if (query == null) {
- throw Error.ArgumentNull("query");
- }
- this.InitializeProviderMode();
- SqlNodeAnnotations annotations = new SqlNodeAnnotations();
- QueryInfo[] qis = this.BuildQuery(query, annotations);
- StringBuilder sb = new StringBuilder();
- for (int i = 0, n = qis.Length; i < n; i++) {
- QueryInfo qi = qis[i];
- #if DEBUG
- StringWriter writer = new StringWriter(System.Globalization.CultureInfo.InvariantCulture);
- DbCommand cmd = this.conManager.Connection.CreateCommand();
- cmd.CommandText = qi.CommandText;
- AssignParameters(cmd, qi.Parameters, null, null);
- LogCommand(writer, cmd);
- sb.Append(writer.ToString());
- #else
- sb.Append(qi.CommandText);
- sb.AppendLine();
- #endif
- }
- return sb.ToString();
- }
- DbCommand IProvider.GetCommand(Expression query) {
- this.CheckDispose();
- this.CheckInitialized();
- if (query == null) {
- throw Error.ArgumentNull("query");
- }
- this.InitializeProviderMode();
- SqlNodeAnnotations annotations = new SqlNodeAnnotations();
- QueryInfo[] qis = this.BuildQuery(query, annotations);
- QueryInfo qi = qis[qis.Length - 1];
- DbCommand cmd = this.conManager.Connection.CreateCommand();
- cmd.CommandText = qi.CommandText;
- cmd.Transaction = this.conManager.Transaction;
- cmd.CommandTimeout = this.commandTimeout;
- AssignParameters(cmd, qi.Parameters, null, null);
- return cmd;
- }
- internal class QueryInfo {
- SqlNode query;
- string commandText;
- ReadOnlyCollection<SqlParameterInfo> parameters;
- ResultShape resultShape;
- Type resultType;
- internal QueryInfo(SqlNode query, string commandText, ReadOnlyCollection<SqlParameterInfo> parameters, ResultShape resultShape, Type resultType) {
- this.query = query;
- this.commandText = commandText;
- this.parameters = parameters;
- this.resultShape = resultShape;
- this.resultType = resultType;
- }
- internal SqlNode Query {
- get { return this.query; }
- }
- internal string CommandText {
- get { return this.commandText; }
- }
- internal ReadOnlyCollection<SqlParameterInfo> Parameters {
- get { return this.parameters; }
- }
- internal ResultShape ResultShape {
- get { return this.resultShape; }
- }
- internal Type ResultType {
- get { return this.resultType; }
- }
- }
- internal enum ResultShape {
- Return,
- Singleton,
- Sequence,
- MultipleResults
- }
- private ResultShape GetResultShape(Expression query) {
- LambdaExpression lambda = query as LambdaExpression;
- if (lambda != null) {
- query = lambda.Body;
- }
- if (query.Type == typeof(void)) {
- return ResultShape.Return;
- }
- else if (query.Type == typeof(IMultipleResults)) {
- return ResultShape.MultipleResults;
- }
- bool isSequence = typeof(IEnumerable).IsAssignableFrom(query.Type);
- ProviderType pt = this.typeProvider.From(query.Type);
- bool isScalar = !pt.IsRuntimeOnlyType && !pt.IsApplicationType;
- bool isSingleton = isScalar || !isSequence;
- MethodCallExpression mce = query as MethodCallExpression;
- if (mce != null) {
- // query operators
- if (mce.Method.DeclaringType == typeof(Queryable) ||
- mce.Method.DeclaringType == typeof(Enumerable)) {
- switch (mce.Method.Name) {
- // methods known to produce singletons
- case "First":
- case "FirstOrDefault":
- case "Single":
- case "SingleOrDefault":
- isSingleton = true;
- break;
- }
- }
- else if (mce.Method.DeclaringType == typeof(DataContext)) {
- if (mce.Method.Name == "ExecuteCommand") {
- return ResultShape.Return;
- }
- }
- else if (mce.Method.DeclaringType.IsSubclassOf(typeof(DataContext))) {
- MetaFunction f = this.GetFunction(query);
- if (f != null) {
- if (!f.IsComposable) {
- isSingleton = false;
- }
- else if (isScalar) {
- isSingleton = true;
- }
- }
- }
- else if (mce.Method.DeclaringType == typeof(DataManipulation) && mce.Method.ReturnType == typeof(int)) {
- return ResultShape.Return;
- }
- }
- if (isSingleton) {
- return ResultShape.Singleton;
- }
- else if (isScalar) {
- return ResultShape.Return;
- }
- else {
- return ResultShape.Sequence;
- }
- }
- [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification="Unknown reason.")]
- private Type GetResultType(Expression query) {
- LambdaExpression lambda = query as LambdaExpression;
- if (lambda != null) {
- query = lambda.Body;
- }
- return query.Type;
- }
- internal QueryInfo[] BuildQuery(Expression query, SqlNodeAnnotations annotations) {
- this.CheckDispose();
- // apply maximal funcletization
- query = Funcletizer.Funcletize(query);
- // convert query nodes into sql nodes
- QueryConverter converter = new QueryConverter(this.services, this.typeProvider, this.translator, this.sqlFactory);
- switch (this.Mode) {
- case ProviderMode.Sql2000:
- converter.ConverterStrategy =
- ConverterStrategy.CanUseScopeIdentity |
- ConverterStrategy.CanUseJoinOn |
- ConverterStrategy.CanUseRowStatus;
- break;
- case ProviderMode.Sql2005:
- case ProviderMode.Sql2008:
- converter.ConverterStrategy =
- ConverterStrategy.CanUseScopeIdentity |
- ConverterStrategy.SkipWithRowNumber |
- ConverterStrategy.CanUseRowStatus |
- ConverterStrategy.CanUseJoinOn |
- ConverterStrategy.CanUseOuterApply |
- ConverterStrategy.CanOutputFromInsert;
- break;
- case ProviderMode.SqlCE:
- converter.ConverterStrategy = ConverterStrategy.CanUseOuterApply;
- // Can't set ConverterStrategy.CanUseJoinOn because scalar subqueries in the ON clause
- // can't be converted into anything.
- break;
- }
- SqlNode node = converter.ConvertOuter(query);
- return this.BuildQuery(this.GetResultShape(query), this.GetResultType(query), node, null, annotations);
- }
- [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification="These issues are related to our use of if-then and case statements for node types, which adds to the complexity count however when reviewed they are easy to navigate and understand.")]
- private QueryInfo[] BuildQuery(ResultShape resultShape, Type resultType, SqlNode node, ReadOnlyCollection<Me.SqlParameter> parentParameters, SqlNodeAnnotations annotations) {
- System.Diagnostics.Debug.Assert(resultType != null);
- System.Diagnostics.Debug.Assert(node != null);
- SqlSupersetValidator validator = new SqlSupersetValidator();
- // These are the rules that apply to every SQL tree.
- if (this.checkQueries) {
- validator.AddValidator(new ColumnTypeValidator()); /* Column CLR Type must agree with its Expressions CLR Type */
- validator.AddValidator(new LiteralValidator()); /* Constrain literal Types */
- }
- validator.Validate(node);
- SqlColumnizer columnizer = new SqlColumnizer();
- // resolve member references
- bool canUseOuterApply = (this.Mode == ProviderMode.Sql2005 || this.Mode == ProviderMode.Sql2008 || this.Mode == ProviderMode.SqlCE);
- SqlBinder binder = new SqlBinder(this.translator, this.sqlFactory, this.services.Model, this.services.Context.LoadOptions, columnizer, canUseOuterApply);
- binder.OptimizeLinkExpansions = (optimizationFlags & OptimizationFlags.OptimizeLinkExpansions) != 0;
- binder.SimplifyCaseStatements = (optimizationFlags & OptimizationFlags.SimplifyCaseStatements) != 0;
- binder.PreBinder = delegate(SqlNode n) {
- // convert methods into known reversable operators
- return PreBindDotNetConverter.Convert(n, this.sqlFactory, this.services.Model);
- };
- node = binder.Bind(node);
- if (this.checkQueries) {
- validator.AddValidator(new ExpectNoAliasRefs());
- validator.AddValidator(new ExpectNoSharedExpressions());
- }
- validator.Validate(node);
- node = PostBindDotNetConverter.Convert(node, this.sqlFactory, this.Mode);
- // identify true flow of sql data types
- SqlRetyper retyper = new SqlRetyper(this.typeProvider, this.services.Model);
- node = retyper.Retype(node);
- validator.Validate(node);
- // change CONVERT to special conversions like UNICODE,CHAR,...
- SqlTypeConverter converter = new SqlTypeConverter(this.sqlFactory);
- node = converter.Visit(node);
- validator.Validate(node);
- // transform type-sensitive methods such as LEN (to DATALENGTH), ...
- SqlMethodTransformer methodTransformer = new SqlMethodTransformer(this.sqlFactory);
- node = methodTransformer.Visit(node);
- validator.Validate(node);
- // convert multisets into separate queries
- SqlMultiplexer.Options options = (this.Mode == ProviderMode.Sql2008 ||
- this.Mode == ProviderMode.Sql2005 ||
- this.Mode == ProviderMode.SqlCE)
- ? SqlMultiplexer.Options.EnableBigJoin : SqlMultiplexer.Options.None;
- SqlMultiplexer mux = new SqlMultiplexer(options, parentParameters, this.sqlFactory);
- node = mux.Multiplex(node);
- validator.Validate(node);
- // convert object construction expressions into flat row projections
- SqlFlattener flattener = new SqlFlattener(this.sqlFactory, columnizer);
- node = flattener.Flatten(node);
- validator.Validate(node);
- if (this.mode == ProviderMode.SqlCE) {
- SqlRewriteScalarSubqueries rss = new SqlRewriteScalarSubqueries(this.sqlFactory);
- node = rss.Rewrite(node);
- }
- // Simplify case statements where all alternatives map to the same thing.
- // Doing this before deflator because the simplified results may lead to
- // more deflation opportunities.
- // Doing this before booleanizer because it may convert CASE statements (non-predicates) into
- // predicate expressions.
- // Doing this before reorderer because it may reduce some orders to constant nodes which should not
- // be passed onto ROW_NUMBER.
- node = SqlCaseSimplifier.Simplify(node, this.sqlFactory);
- // Rewrite order-by clauses so that they only occur at the top-most select
- // or in selects with TOP
- SqlReorderer reorderer = new SqlReorderer(this.typeProvider, this.sqlFactory);
- node = reorderer.Reorder(node);
- validator.Validate(node);
- // Inject code to turn predicates into bits, and bits into predicates where necessary
- node = SqlBooleanizer.Rationalize(node, this.typeProvider, this.services.Model);
- if (this.checkQueries) {
- validator.AddValidator(new ExpectRationalizedBooleans()); /* From now on all boolean expressions should remain rationalized. */
- }
- validator.Validate(node);
- if (this.checkQueries) {
- validator.AddValidator(new ExpectNoFloatingColumns());
- }
- // turning predicates into bits/ints can change Sql types, propagate changes
- node = retyper.Retype(node);
- validator.Validate(node);
- // assign aliases to columns
- // we need to do this now so that the sql2k lifters will work
- SqlAliaser aliaser = new SqlAliaser();
- node = aliaser.AssociateColumnsWithAliases(node);
- validator.Validate(node);
- // SQL2K enablers.
- node = SqlLiftWhereClauses.Lift(node, this.typeProvider, this.services.Model);
- node = SqlLiftIndependentRowExpressions.Lift(node);
- node = SqlOuterApplyReducer.Reduce(node, this.sqlFactory, annotations);
- node = SqlTopReducer.Reduce(node, annotations, this.sqlFactory);
- // resolve references to columns in other scopes by adding them
- // to the intermediate selects
- SqlResolver resolver = new SqlResolver();
- node = resolver.Resolve(node);
- validator.Validate(node);
- // re-assign aliases after resolving (new columns may have been added)
- node = aliaser.AssociateColumnsWithAliases(node);
- validator.Validate(node);
- // fixup union projections
- node = SqlUnionizer.Unionize(node);
- // remove order-by of literals
- node = SqlRemoveConstantOrderBy.Remove(node);
- // throw out unused columns and redundant sub-queries...
- SqlDeflator deflator = new SqlDeflator();
- node = deflator.Deflate(node);
- validator.Validate(node);
- // Positioning after deflator because it may remove unnecessary columns
- // from SELECT projection lists and allow more CROSS APPLYs to be reduced
- // to CROSS JOINs.
- node = SqlCrossApplyToCrossJoin.Reduce(node, annotations);
- // fixup names for aliases, columns, locals, etc..
- SqlNamer namer = new SqlNamer();
- node = namer.AssignNames(node);
- validator.Validate(node);
- // Convert [N]Text,Image to [N]VarChar(MAX),VarBinary(MAX) where necessary.
- // These new types do not exist on SQL2k, so add annotations.
- LongTypeConverter longTypeConverter = new LongTypeConverter(this.sqlFactory);
- node = longTypeConverter.AddConversions(node, annotations);
-
- // final validation
- validator.AddValidator(new ExpectNoMethodCalls());
- validator.AddValidator(new ValidateNoInvalidComparison());
- validator.Validate(node);
- SqlParameterizer parameterizer = new SqlParameterizer(this.typeProvider, annotations);
- SqlFormatter formatter = new SqlFormatter();
- if (this.mode == ProviderMode.SqlCE ||
- this.mode == ProviderMode.Sql2005 ||
- this.mode == ProviderMode.Sql2008) {
- formatter.ParenthesizeTop = true;
- }
- SqlBlock block = node as SqlBlock;
- if (block != null && this.mode == ProviderMode.SqlCE) {
- // SQLCE cannot batch multiple statements.
- ReadOnlyCollection<ReadOnlyCollection<SqlParameterInfo>> parameters = parameterizer.ParameterizeBlock(block);
- string[] commands = formatter.FormatBlock(block, false);
- QueryInfo[] queries = new QueryInfo[commands.Length];
- for (int i = 0, n = commands.Length; i < n; i++) {
- queries[i] = new QueryInfo(
- block.Statements[i],
- commands[i],
- parameters[i],
- (i < n - 1) ? ResultShape.Return : resultShape,
- (i < n - 1) ? typeof(int) : resultType
- );
- }
- return queries;
- }
- else {
- // build only one result
- ReadOnlyCollection<SqlParameterInfo> parameters = parameterizer.Parameterize(node);
- string commandText = formatter.Format(node);
- return new QueryInfo[] {
- new QueryInfo(node, commandText, parameters, resultShape, resultType)
- };
- }
- }
- private SqlSelect GetFinalSelect(SqlNode node) {
- switch (node.NodeType) {
- case SqlNodeType.Select:
- return (SqlSelect)node;
- case SqlNodeType.Block: {
- SqlBlock b = (SqlBlock)node;
- return GetFinalSelect(b.Statements[b.Statements.Count - 1]);
- }
- }
- return null;
- }
- private IObjectReaderFactory GetReaderFactory(SqlNode node, Type elemType) {
- SqlSelect sel = node as SqlSelect;
- SqlExpression projection = null;
- if (sel == null && node.NodeType == SqlNodeType.Block) {
- sel = this.GetFinalSelect(node);
- }
- if (sel != null) {
- projection = sel.Selection;
- }
- else {
- SqlUserQuery suq = node as SqlUserQuery;
- if (suq != null && suq.Projection != null) {
- projection = suq.Projection;
- }
- }
- IObjectReaderFactory factory;
- if (projection != null) {
- factory = this.readerCompiler.Compile(projection, elemType);
- }
- else {
- return this.GetDefaultFactory(services.Model.GetMetaType(elemType));
- }
- return factory;
- }
- private IObjectReaderFactory GetDefaultFactory(MetaType rowType) {
- if (rowType == null) {
- throw Error.ArgumentNull("rowType");
- }
- SqlNodeAnnotations annotations = new SqlNodeAnnotations();
- Expression tmp = Expression.Constant(null);
- SqlUserQuery suq = new SqlUserQuery(string.Empty, null, null, tmp);
- if (TypeSystem.IsSimpleType(rowType.Type)) {
- // if the element type is a simple type (int, bool, etc.) we create
- // a single column binding
- SqlUserColumn col = new SqlUserColumn(rowType.Type, typeProvider.From(rowType.Type), suq, "", false, suq.SourceExpression);
- suq.Columns.Add(col);
- suq.Projection = col;
- }
- else {
- // ... otherwise we generate a default projection
- SqlUserRow rowExp = new SqlUserRow(rowType.InheritanceRoot, this.typeProvider.GetApplicationType((int)ConverterSpecialTypes.Row), suq, tmp);
- suq.Projection = this.translator.BuildProjection(rowExp, rowType, true, null, tmp);
- }
- Type resultType = TypeSystem.GetSequenceType(rowType.Type);
- QueryInfo[] qis = this.BuildQuery(ResultShape.Sequence, resultType, suq, null, annotations);
- return this.GetReaderFactory(qis[qis.Length - 1].Query, rowType.Type);
- }
- class CompiledQuery : ICompiledQuery {
- DataLoadOptions originalShape;
- Expression query;
- QueryInfo[] queryInfos;
- IObjectReaderFactory factory;
- ICompiledSubQuery[] subQueries;
- internal CompiledQuery(SqlProvider provider, Expression query, QueryInfo[] queryInfos, IObjectReaderFactory factory, ICompiledSubQuery[] subQueries) {
- this.originalShape = provider.services.Context.LoadOptions;
- this.query = query;
- this.queryInfos = queryInfos;
- this.factory = factory;
- this.subQueries = subQueries;
- }
- public IExecuteResult Execute(IProvider provider, object[] arguments) {
- if (provider == null) {
- throw Error.ArgumentNull("provider");
- }
- SqlProvider sqlProvider = provider as SqlProvider;
- if (sqlProvider == null) {
- throw Error.ArgumentTypeMismatch("provider");
- }
- // verify shape is compatibile with original.
- if (!AreEquivalentShapes(this.originalShape, sqlProvider.services.Context.LoadOptions)) {
- throw Error.CompiledQueryAgainstMultipleShapesNotSupported();
- }
- // execute query (only last query produces results)
- return sqlProvider.ExecuteAll(this.query, this.queryInfos, this.factory, arguments, subQueries);
- }
- private static bool AreEquivalentShapes(DataLoadOptions shape1, DataLoadOptions shape2) {
- if (shape1 == shape2) {
- return true;
- }
- else if (shape1 == null) {
- return shape2.IsEmpty;
- }
- else if (shape2 == null) {
- return shape1.IsEmpty;
- }
- else if (shape1.IsEmpty && shape2.IsEmpty) {
- return true;
- }
- return false;
- }
- }
- class CompiledSubQuery : ICompiledSubQuery {
- QueryInfo queryInfo;
- IObjectReaderFactory factory;
- ReadOnlyCollection<Me.SqlParameter> parameters;
- ICompiledSubQuery[] subQueries;
- internal CompiledSubQuery(QueryInfo queryInfo, IObjectReaderFactory factory, ReadOnlyCollection<Me.SqlParameter> parameters, ICompiledSubQuery[] subQueries) {
- this.queryInfo = queryInfo;
- this.factory = factory;
- this.parameters = parameters;
- this.subQueries = subQueries;
- }
- public IExecuteResult Execute(IProvider provider, object[] parentArgs, object[] userArgs) {
- if (parentArgs == null && !(this.parameters == null || this.parameters.Count == 0)) {
- throw Error.ArgumentNull("arguments");
- }
- SqlProvider sqlProvider = provider as SqlProvider;
- if (sqlProvider == null) {
- throw Error.ArgumentTypeMismatch("provider");
- }
- // construct new copy of query info
- List<SqlParameterInfo> spis = new List<SqlParameterInfo>(this.queryInfo.Parameters);
- // add call arguments
- for (int i = 0, n = this.parameters.Count; i < n; i++) {
- spis.Add(new SqlParameterInfo(this.parameters[i], parentArgs[i]));
- }
- QueryInfo qi = new QueryInfo(
- this.queryInfo.Query,
- this.queryInfo.CommandText,
- spis.AsReadOnly(),
- this.queryInfo.ResultShape,
- this.queryInfo.ResultType
- );
- // execute query
- return sqlProvider.Execute(null, qi, this.factory, parentArgs, userArgs, subQueries, null);
- }
- }
- class ExecuteResult : IExecuteResult, IDisposable {
- DbCommand command;
- ReadOnlyCollection<SqlParameterInfo> parameters;
- IObjectReaderSession session;
- int iReturnParameter = -1;
- object value;
- [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields", Justification = "Microsoft: used in an assert in ReturnValue.set")]
- bool useReturnValue;
- bool isDisposed;
- internal ExecuteResult(DbCommand command, ReadOnlyCollection<SqlParameterInfo> parameters, IObjectReaderSession session, object value, bool useReturnValue)
- : this(command, parameters, session) {
- this.value = value;
- this.useReturnValue = useReturnValue;
- if (this.command != null && this.parameters != null && useReturnValue) {
- iReturnParameter = GetParameterIndex("@RETURN_VALUE");
- }
- }
- internal ExecuteResult(DbCommand command, ReadOnlyCollection<SqlParameterInfo> parameters, IObjectReaderSession session) {
- this.command = command;
- this.parameters = parameters;
- this.session = session;
- }
- internal ExecuteResult(DbCommand command, ReadOnlyCollection<SqlParameterInfo> parameters, IObjectReaderSession session, object value)
- : this(command, parameters, session, value, false) {
- }
- [SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "value", Justification="FxCop Error -- False positive during code analysis")]
- public object ReturnValue {
- get {
- if (this.iReturnParameter >= 0) {
- return this.GetParameterValue(this.iReturnParameter);
- }
- return this.value;
- }
- internal set {
- Debug.Assert(!useReturnValue);
- this.value = value;
- }
- }
- private int GetParameterIndex(string paramName) {
- int idx = -1;
- for (int i = 0, n = this.parameters.Count; i < n; i++) {
- if (string.Compare(parameters[i].Parameter.Name, paramName, StringComparison.OrdinalIgnoreCase) == 0) {
- idx = i;
- break;
- }
- }
- return idx;
- }
- internal object GetParameterValue(string paramName) {
- int idx = GetParameterIndex(paramName);
- if (idx >= 0) {
- return GetParameterValue(idx);
- }
- return null;
- }
- public object GetParameterValue(int parameterIndex) {
- if (this.parameters == null || parameterIndex < 0 || parameterIndex > this.parameters.Count) {
- throw Error.ArgumentOutOfRange("parameterIndex");
- }
- // SQL server requires all results to be read before output parameters are visible
- if (this.session != null && !this.session.IsBuffered) {
- this.session.Buffer();
- }
- SqlParameterInfo pi = this.parameters[parameterIndex];
- object parameterValue = this.command.Parameters[parameterIndex].Value;
- if (parameterValue == DBNull.Value) parameterValue = null;
- if (parameterValue != null && parameterValue.GetType() != pi.Parameter.ClrType) {
- return DBConvert.ChangeType(parameterValue, pi.Parameter.ClrType);
- }
- return parameterValue;
- }
- public void Dispose() {
- if (!this.isDisposed) {
- // Technically, calling GC.SuppressFinalize is not required because the class does not
- // have a finalizer, but it does no harm, protects against the case where a finalizer is added
- // in the future, and prevents an FxCop warning.
- GC.SuppressFinalize(this);
- this.isDisposed = true;
- if (this.session!=null) {
- this.session.Dispose();
- }
- }
- }
- }
- class SequenceOfOne<T> : IEnumerable<T>, IEnumerable {
- T[] sequence;
- internal SequenceOfOne(T value) {
- this.sequence = new T[] { value };
- }
- public IEnumerator<T> GetEnumerator() {
- return ((IEnumerable<T>)this.sequence).GetEnumerator();
- }
- IEnumerator IEnumerable.GetEnumerator() {
- return this.GetEnumerator();
- }
- }
- class OneTimeEnumerable<T> : IEnumerable<T>, IEnumerable {
- IEnumerator<T> enumerator;
- internal OneTimeEnumerable(IEnumerator<T> enumerator) {
- System.Diagnostics.Debug.Assert(enumerator != null);
- this.enumerator = enumerator;
- }
- public IEnumerator<T> GetEnumerator() {
- if (this.enumerator == null) {
- throw Error.CannotEnumerateResultsMoreThanOnce();
- }
- IEnumerator<T> e = this.enumerator;
- this.enumerator = null;
- return e;
- }
- IEnumerator IEnumerable.GetEnumerator() {
- return this.GetEnumerator();
- }
- }
- /// <summary>
- /// Result type for single rowset returning stored procedures.
- /// </summary>
- class SingleResult<T> : ISingleResult<T>, IDisposable, IListSource {
- private IEnumerable<T> enumerable;
- private ExecuteResult executeResult;
- private DataContext context;
- private IBindingList cachedList;
- internal SingleResult(IEnumerable<T> enumerable, ExecuteResult executeResult, DataContext context) {
- System.Diagnostics.Debug.Assert(enumerable != null);
- System.Diagnostics.Debug.Assert(executeResult != null);
- this.enumerable = enumerable;
- this.executeResult = executeResult;
- this.context = context;
- }
- public IEnumerator<T> GetEnumerator() {
- return enumerable.GetEnumerator();
- }
- IEnumerator IEnumerable.GetEnumerator() {
- return this.GetEnumerator();
- }
- public object ReturnValue {
- get {
- return executeResult.GetParameterValue("@RETURN_VALUE");
- }
- }
- public void Dispose() {
- // Technically, calling GC.SuppressFinalize is not required because the class does not
- // have a finalizer, but it does no harm, protects against the case where a finalizer is added
- // in the future, and prevents an FxCop warning.
- GC.SuppressFinalize(this);
- this.executeResult.Dispose();
- }
- IList IListSource.GetList() {
- if (this.cachedList == null) {
- this.cachedList = BindingList.Create<T>(this.context, this);
- }
- return this.cachedList;
- }
- bool IListSource.ContainsListCollection {
- get { return false; }
- }
- }
- class MultipleResults : IMultipleResults, IDisposable {
- SqlProvider provider;
- MetaFunction function;
- IObjectReaderSession session;
- bool isDisposed;
- private ExecuteResult executeResult;
- internal MultipleResults(SqlProvider provider, MetaFunction function, IObjectReaderSession session, ExecuteResult executeResult) {
- this.provider = provider;
- this.function = function;
- this.session = session;
- this.executeResult = executeResult;
- }
- public IEnumerable<T> GetResult<T>() {
- MetaType metaType = null;
- // Check the inheritance hierarchy of each mapped result row type
- // for the function.
- if (this.function != null) {
- foreach (MetaType mt in function.ResultRowTypes) {
- metaType = mt.InheritanceTypes.SingleOrDefault(it => it.Type == typeof(T));
- if (metaType != null) {
- break;
- }
- }
- }
- if (metaType == null) {
- metaType = this.provider.services.Model.GetMetaType(typeof(T));
- }
- IObjectReaderFactory factory = this.provider.GetDefaultFactory(metaType);
- IObjectReader objReader = factory.GetNextResult(this.session, false);
- if (objReader == null) {
- this.Dispose();
- return null;
- }
- return new SingleResult<T>(new OneTimeEnumerable<T>((IEnumerator<T>)objReader), this.executeResult, this.provider.services.Context);
- }
- public void Dispose() {
- if (!this.isDisposed) {
- // Technically, calling GC.SuppressFinalize is not required because the class does not
- // have a finalizer, but it does no harm, protects against the case where a finalizer is added
- // in the future, and prevents an FxCop warning.
- GC.SuppressFinalize(this);
- this.isDisposed = true;
- if (this.executeResult != null) {
- this.executeResult.Dispose();
- }
- else {
- this.session.Dispose();
- }
- }
- }
- public object ReturnValue {
- get {
- if (this.executeResult != null) {
- return executeResult.GetParameterValue("@RETURN_VALUE");
- } else {
- return null;
- }
- }
- }
- }
- }
- }