EntityConnection.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataEntity / System / Data / EntityClient / EntityConnection.cs / 1305376 / EntityConnection.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner  [....]
// @backupOwner [....] 
//--------------------------------------------------------------------- 
using System.Collections;
using System.Collections.Generic; 
using System.Configuration;
using System.Data;
using System.Data.Common;
using System.Data.Common.EntitySql; 
using System.Data.Mapping;
using System.Data.Metadata; 
using System.Data.Metadata.Edm; 
using System.Diagnostics;
using System.IO; 
using System.Text;
using System.Transactions;
using System.Xml;
using System.Data.Common.CommandTrees; 
using System.Runtime.Versioning;
using System.Linq; 
 

namespace System.Data.EntityClient 
{
    /// 
    /// Class representing a connection for the conceptual layer. An entity connection may only
    /// be initialized once (by opening the connection). It is subsequently not possible to change 
    /// the connection string, attach a new store connection, or change the store connection string.
    ///  
    public sealed class EntityConnection : DbConnection 
    {
        #region Constants 

        private const string s_metadataPathSeparator = "|";
        private const string s_semicolonSeparator = ";";
        private const string s_entityClientProviderName = "System.Data.EntityClient"; 
        private const string s_providerInvariantName = "provider";
        private const string s_providerConnectionString = "provider connection string"; 
        private const string s_readerPrefix = "reader://"; 

        #endregion 

        private readonly object _connectionStringLock = new object();
        private static readonly DbConnectionOptions s_emptyConnectionOptions = new DbConnectionOptions(String.Empty, null, false);
 
        // The connection options object having the connection settings needed by this connection
        private DbConnectionOptions _userConnectionOptions; 
        private DbConnectionOptions _effectiveConnectionOptions; 

        // The internal connection state of the entity client, which is distinct from that of the 
        // store connection it aggregates.
        private ConnectionState _entityClientConnectionState = ConnectionState.Closed;

        private DbProviderFactory _providerFactory; 
        private DbConnection _storeConnection;
        private readonly bool _userOwnsStoreConnection; 
        private MetadataWorkspace _metadataWorkspace; 
        private EntityTransaction _currentTransaction;
        private bool _initialized; 
        // will only have a value while waiting for the ssdl to be loaded. we should
        // never have a value for this when _initialized == true
        private MetadataArtifactLoader _artifactLoader;
 
        private static int _objectTypeCount; // Bid counter
        internal readonly int ObjectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); 
 
        /// 
        /// Constructs the EntityConnection object with a connection not yet associated to a particular store 
        /// 
        [ResourceExposure(ResourceScope.None)] //We are not exposing any resource
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] //For EntityConnection constructor. But since the connection string we pass in is an Empty String,
                                                     //we consume the resource and do not expose it any further. 
        public EntityConnection()
            : this(String.Empty) 
        { 
        }
 
        /// 
        /// Constructs the EntityConnection object with a connection string
        /// 
        /// The connection string, may contain a list of settings for the connection or 
        /// just the name of the connection to use
        [ResourceExposure(ResourceScope.Machine)] //Exposes the file names as part of ConnectionString which are a Machine resource 
        [ResourceConsumption(ResourceScope.Machine)] //For ChangeConnectionString method call. But the paths are not created in this method. 
        public EntityConnection(string connectionString)
        { 
            GC.SuppressFinalize(this);
            this.ChangeConnectionString(connectionString);
        }
 
        /// 
        /// Constructs the EntityConnection from Metadata loaded in memory 
        ///  
        /// Workspace containing metadata information.
        public EntityConnection(MetadataWorkspace workspace, DbConnection connection) 
        {
            EntityUtil.CheckArgumentNull(workspace, "workspace");
            EntityUtil.CheckArgumentNull(connection, "connection");
 

            if (!workspace.IsItemCollectionAlreadyRegistered(DataSpace.CSpace)) 
            { 
                throw EntityUtil.Argument(System.Data.Entity.Strings.EntityClient_ItemCollectionsNotRegisteredInWorkspace("EdmItemCollection"));
            } 
            if(!workspace.IsItemCollectionAlreadyRegistered(DataSpace.SSpace))
            {
                throw EntityUtil.Argument(System.Data.Entity.Strings.EntityClient_ItemCollectionsNotRegisteredInWorkspace("StoreItemCollection"));
            } 
            if(!workspace.IsItemCollectionAlreadyRegistered(DataSpace.CSSpace))
            { 
                throw EntityUtil.Argument(System.Data.Entity.Strings.EntityClient_ItemCollectionsNotRegisteredInWorkspace("StorageMappingItemCollection")); 
            }
 
            if (connection.State != ConnectionState.Closed)
            {
                throw EntityUtil.Argument(System.Data.Entity.Strings.EntityClient_ConnectionMustBeClosed);
            } 

            if (connection.ProviderFactory == null) 
            { 
                throw EntityUtil.ProviderIncompatible(System.Data.Entity.Strings.EntityClient_DbConnectionHasNoProvider(connection));
            } 

            StoreItemCollection collection = (StoreItemCollection)workspace.GetItemCollection(DataSpace.SSpace);
            GC.SuppressFinalize(this);
 
            _providerFactory = collection.StoreProviderFactory;
            _storeConnection = connection; 
            _userOwnsStoreConnection = true; 
            _metadataWorkspace = workspace;
            _initialized = true; 
        }

        /// 
        /// Get or set the entity connection string associated with this connection object 
        /// 
        public override string ConnectionString 
        { 
            get
            { 
                //EntityConnection created using MetadataWorkspace
                // _userConnectionOptions is not null when empty Constructor is used
                // Therefore it is sufficient to identify whether EC(MW, DbConnection) is used
                if (this._userConnectionOptions == null) 
                {
                    Debug.Assert(_storeConnection != null); 
 
                    string invariantName;
                    if(!EntityUtil.TryGetProviderInvariantName(_storeConnection.ProviderFactory, out invariantName)) 
                    {
                        Debug.Fail("Provider Invariant Name not found");
                        invariantName = "";
                    } 

                    return string.Format(Globalization.CultureInfo.InvariantCulture, 
                        "{0}={3}{4};{1}={5};{2}=\"{6}\";", 
                        EntityConnectionStringBuilder.MetadataParameterName,
                        s_providerInvariantName, 
                        s_providerConnectionString,
                        s_readerPrefix,
                        _metadataWorkspace.MetadataWorkspaceId,
                        invariantName, 
                        FormatProviderString(_storeConnection.ConnectionString));
                } 
 
                string userConnectionString = this._userConnectionOptions.UsersConnectionString(false);
 
                // In here, we ask the store connection for the connection string only if the user didn't specify a name
                // connection (meaning effective connection options == user connection options).  If the user specified a
                // named connection, then we return just that.  Otherwise, if the connection string is different from what
                // we have in the connection options, which is possible if the store connection changed the connection 
                // string to hide the password, then we use the builder to reconstruct the string. The parameters will be
                // shuffled, which is unavoidable but it's ok because the connection string cannot be the same as what the 
                // user originally passed in anyway.  However, if the store connection string is still the same, then we 
                // simply return what the user originally passed in.
                if (object.ReferenceEquals(_userConnectionOptions, _effectiveConnectionOptions) && this._storeConnection != null) 
                {
                    string storeConnectionString = null;
                    try
                    { 
                        storeConnectionString = this._storeConnection.ConnectionString;
                    } 
                    catch (Exception e) 
                    {
                        if (EntityUtil.IsCatchableExceptionType(e)) 
                        {
                            throw EntityUtil.Provider(@"ConnectionString", e);
                        }
                        throw; 
                    }
 
                    // SQLBU 514721, 515024 - Defer connection string parsing to ConnectionStringBuilder 
                    // if the 'userStoreConnectionString' and 'storeConnectionString' are unequal, except
                    // when they are both null or empty (we treat null and empty as equivalent here). 
                    //
                    string userStoreConnectionString = this._userConnectionOptions[EntityConnectionStringBuilder.ProviderConnectionStringParameterName];
                    if ((storeConnectionString != userStoreConnectionString)
                        && !(string.IsNullOrEmpty(storeConnectionString) && string.IsNullOrEmpty(userStoreConnectionString))) 
                    {
                        // Feeds the connection string into the connection string builder, then plug in the provider connection string into 
                        // the builder, and then extract the string from the builder 
                        EntityConnectionStringBuilder connectionStringBuilder = new EntityConnectionStringBuilder(userConnectionString);
                        connectionStringBuilder.ProviderConnectionString = storeConnectionString; 
                        return connectionStringBuilder.ConnectionString;
                    }
                }
 
                return userConnectionString;
            } 
            [ResourceExposure(ResourceScope.Machine)] //Exposes the file names as part of ConnectionString which are a Machine resource 
            [ResourceConsumption(ResourceScope.Machine)] //For ChangeConnectionString method call. But the paths are not created in this method.
            set 
            {
                ValidateChangesPermitted();
                this.ChangeConnectionString(value);
            } 
        }
 
        ///  
        /// Formats provider string to replace " with \" so it can be appended within quotation marks "..."
        ///  
        private static string FormatProviderString(string providerString)
        {
            return providerString.Trim().Replace("\"", "\\\"");
        } 

        ///  
        /// Get the time to wait when attempting to establish a connection before ending the try and generating an error 
        /// 
        public override int ConnectionTimeout 
        {
            get
            {
                if (this._storeConnection == null) 
                    return 0;
 
                try 
                {
                    return this._storeConnection.ConnectionTimeout; 
                }
                catch (Exception e)
                {
                    if (EntityUtil.IsCatchableExceptionType(e)) 
                    {
                        throw EntityUtil.Provider(@"ConnectionTimeout", e); 
                    } 
                    throw;
                } 
            }
        }

        ///  
        /// Get the name of the current database or the database that will be used after a connection is opened
        ///  
        public override string Database 
        {
            get 
            {
                return String.Empty;
            }
        } 

        ///  
        /// Gets the ConnectionState property of the EntityConnection 
        /// 
        public override ConnectionState State 
        {
            get
            {
                try 
                {
                    if (this._entityClientConnectionState == ConnectionState.Open) 
                    { 
                        Debug.Assert(this.StoreConnection != null);
                        if (this.StoreConnection.State != ConnectionState.Open) 
                        {
                            return ConnectionState.Broken;
                        }
                    } 
                    return this._entityClientConnectionState;
                } 
                catch (Exception e) 
                {
                    if (EntityUtil.IsCatchableExceptionType(e)) 
                    {
                        throw EntityUtil.Provider(@"State", e);
                    }
                    throw; 
                }
            } 
        } 

        ///  
        /// Gets the name or network address of the data source to connect to
        /// 
        public override string DataSource
        { 
            get
            { 
                if (this._storeConnection == null) 
                    return String.Empty;
 
                try
                {
                    return this._storeConnection.DataSource;
                } 
                catch (Exception e)
                { 
                    if (EntityUtil.IsCatchableExceptionType(e)) 
                    {
                        throw EntityUtil.Provider(@"DataSource", e); 
                    }
                    throw;
                }
            } 
        }
 
        ///  
        /// Gets a string that contains the version of the data store to which the client is connected
        ///  
        public override string ServerVersion
        {
            get
            { 
                if (this._storeConnection == null)
                    throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_ConnectionStringNeededBeforeOperation); 
 
                if (this.State != ConnectionState.Open)
                { 
                    throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_ConnectionNotOpen);
                }

                try 
                {
                    return this._storeConnection.ServerVersion; 
                } 
                catch (Exception e)
                { 
                    if (EntityUtil.IsCatchableExceptionType(e))
                    {
                        throw EntityUtil.Provider(@"ServerVersion", e);
                    } 
                    throw;
                } 
            } 
        }
 
        /// 
        /// Gets the provider factory associated with EntityConnection
        /// 
        override protected DbProviderFactory DbProviderFactory 
        {
            get 
            { 
                return EntityProviderFactory.Instance;
            } 
        }

        /// 
        /// Gets the DbProviderFactory for the underlying provider 
        /// 
        internal DbProviderFactory StoreProviderFactory 
        { 
            get
            { 
                return this._providerFactory;
            }
        }
 
        /// 
        /// Gets the DbConnection for the underlying provider connection 
        ///  
        public DbConnection StoreConnection
        { 
            get
            {
                return this._storeConnection;
            } 
        }
 
        ///  
        /// Gets the metadata workspace used by this connection
        ///  
        [CLSCompliant(false)]
        public MetadataWorkspace GetMetadataWorkspace()
        {
            return GetMetadataWorkspace(true /* initializeAllCollections */); 
        }
 
        private bool ShouldRecalculateMetadataArtifactLoader(List loaders) 
        {
            if (loaders.Any(loader => loader.GetType() == typeof(MetadataArtifactLoaderCompositeFile))) 
            {
                // the loaders had folders in it
                return true;
            } 
            // in the case that loaders only contains resources or file name, we trust the cache
            return false; 
        } 

        [ResourceExposure(ResourceScope.None)] //The resource( path name) is not exposed to the callers of this method 
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] //Fir SplitPaths call and we pick the file names from class variable.
        internal MetadataWorkspace GetMetadataWorkspace(bool initializeAllCollections)
        {
            Debug.Assert(_metadataWorkspace != null || _effectiveConnectionOptions != null, "The effective connection options is null, which should never be"); 
            if (_metadataWorkspace == null ||
                (initializeAllCollections && !_metadataWorkspace.IsItemCollectionAlreadyRegistered(DataSpace.SSpace))) 
            { 
                // This lock is to ensure that the connection string and the metadata workspace are in a consistent state, that is, you
                // don't get a metadata workspace not matching what's described by the connection string 
                lock (_connectionStringLock)
                {
                    EdmItemCollection edmItemCollection = null;
                    if (_metadataWorkspace == null) 
                    {
                        MetadataWorkspace workspace = new MetadataWorkspace(); 
                        List loaders = new List(); 
                        string paths = _effectiveConnectionOptions[EntityConnectionStringBuilder.MetadataParameterName];
 
                        if (!string.IsNullOrEmpty(paths))
                        {
                            loaders = MetadataCache.GetOrCreateMetdataArtifactLoader(paths);
 
                            if(!ShouldRecalculateMetadataArtifactLoader(loaders))
                            { 
                                _artifactLoader = MetadataArtifactLoader.Create(loaders); 
                            }
                            else 
                            {
                                // the loaders contains folders that might get updated during runtime, so we have to recalculate the loaders again
                                _artifactLoader = MetadataArtifactLoader.Create(MetadataCache.SplitPaths(paths));
                            } 
                        }
                        else 
                        { 
                            _artifactLoader = MetadataArtifactLoader.Create(loaders);
                        } 

                        edmItemCollection = LoadEdmItemCollection(workspace, _artifactLoader);
                        _metadataWorkspace = workspace;
                    } 
                    else
                    { 
                        edmItemCollection = (EdmItemCollection)_metadataWorkspace.GetItemCollection(DataSpace.CSpace); 
                    }
 
                    if (initializeAllCollections && !_metadataWorkspace.IsItemCollectionAlreadyRegistered(DataSpace.SSpace))
                    {
                        LoadStoreItemCollections(_metadataWorkspace, _storeConnection, _providerFactory, _effectiveConnectionOptions, edmItemCollection, _artifactLoader);
                        _artifactLoader = null; 
                        _initialized = true;
                    } 
                } 
            }
 
            return _metadataWorkspace;
        }

        ///  
        /// Gets the current transaction that this connection is enlisted in
        ///  
        internal EntityTransaction CurrentTransaction 
        {
            get 
            {
                // Null out the current transaction if the state is closed or zombied
                if ((null != _currentTransaction) && ((null == _currentTransaction.StoreTransaction.Connection) || (this.State == ConnectionState.Closed)))
                { 
                    ClearCurrentTransaction();
                } 
                return _currentTransaction; 
            }
        } 

        /// 
        /// Establish a connection to the data store by calling the Open method on the underlying data provider
        ///  
        public override void Open()
        { 
            if (this._storeConnection == null) 
                throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_ConnectionStringNeededBeforeOperation);
 
            if (this.State != ConnectionState.Closed)
            {
                throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_CannotReopenConnection);
            } 

            bool closeStoreConnectionOnFailure = false; 
            OpenStoreConnectionIf(this._storeConnection.State != ConnectionState.Open, 
                                  this._storeConnection,
                                  null, 
                                  EntityRes.EntityClient_ProviderSpecificError,
                                  @"Open",
                                  ref closeStoreConnectionOnFailure);
 
            // the following guards against the case when the user closes the underlying store connection
            // in the state change event handler, as a consequence of which we are in the 'Broken' state 
            if (this._storeConnection == null || this._storeConnection.State != ConnectionState.Open) 
            {
                throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_ConnectionNotOpen); 
            }

            InitializeMetadata(this._storeConnection, this._storeConnection, closeStoreConnectionOnFailure);
            SetEntityClientConnectionStateToOpen(); 
        }
 
        ///  
        /// Helper method that conditionally opens a specified store connection
        ///  
        /// The condition to evaluate
        /// The store connection to open
        /// The original store connection associated with the entity client
        /// A flag that is set on if the connection is opened 
        /// successfully
        private void OpenStoreConnectionIf(bool openCondition, 
                                           DbConnection storeConnectionToOpen, 
                                           DbConnection originalConnection,
                                           string exceptionCode, 
                                           string attemptedOperation,
                                           ref bool closeStoreConnectionOnFailure)
        {
            try 
            {
                if (openCondition) 
                { 
                    storeConnectionToOpen.Open();
                    closeStoreConnectionOnFailure = true; 
                }

                ResetStoreConnection(storeConnectionToOpen, originalConnection, false);
 
                // With every successful open of the store connection, always null out the current
                // transaction (if there is one) 
                ClearCurrentTransaction(); 
            }
            catch (Exception e) 
            {
                if (EntityUtil.IsCatchableExceptionType(e))
                {
                    string exceptionMessage = string.IsNullOrEmpty(attemptedOperation) ? 
                        EntityRes.GetString(exceptionCode) :
                        EntityRes.GetString(exceptionCode, attemptedOperation); 
 
                    throw EntityUtil.ProviderExceptionWithMessage(exceptionMessage, e);
                } 
                throw;
            }
        }
 
        /// 
        /// Helper method to initialize the metadata workspace and reset the store connection 
        /// associated with the entity client 
        /// 
        /// The new connection to associate with the entity client 
        /// The original connection associated with the entity client
        /// A flag to indicate whether the original
        /// store connection needs to be closed on failure
        private void InitializeMetadata(DbConnection newConnection, 
                                        DbConnection originalConnection,
                                        bool closeOriginalConnectionOnFailure) 
        { 
            try
            { 
                // Ensure metadata is loaded and the workspace is appropriately initialized.
                GetMetadataWorkspace();
            }
            catch (Exception ex) 
            {
                // Undo the open if something failed 
                if (EntityUtil.IsCatchableExceptionType(ex)) 
                {
                    ResetStoreConnection(newConnection, originalConnection, closeOriginalConnectionOnFailure); 
                }
                throw;
            }
        } 

        ///  
        /// Set the entity client connection state to Open, and raise an appropriate event 
        /// 
        private void SetEntityClientConnectionStateToOpen() 
        {
            this._entityClientConnectionState = ConnectionState.Open;
            OnStateChange(System.Data.ProviderBase.DbConnectionInternal.StateChangeOpen);
        } 

        ///  
        /// This method sets the store connection and hooks up the event 
        /// 
        /// The  DbConnection to set 
        /// The original DbConnection to be closed - this argument could be null
        /// Indicates whether the original store connection should be closed
        private void ResetStoreConnection(DbConnection newConnection, DbConnection originalConnection, bool closeOriginalConnection)
        { 
            this._storeConnection = newConnection;
 
            if (closeOriginalConnection && (originalConnection != null)) 
            {
                originalConnection.Close(); 
            }
        }

        ///  
        /// Create a new command object that uses this connection object
        ///  
        public new EntityCommand CreateCommand() 
        {
            return new EntityCommand(null, this); 
        }

        /// 
        /// Create a new command object that uses this connection object 
        /// 
        protected override DbCommand CreateDbCommand() 
        { 
            return this.CreateCommand();
        } 

        /// 
        /// Close the connection to the data store
        ///  
        public override void Close()
        { 
            // It's a no-op if there isn't an underlying connection 
            if (this._storeConnection == null)
                return; 

            this.CloseHelper();
        }
 
        /// 
        /// Changes the current database for this connection 
        ///  
        /// The name of the database to change to
        public override void ChangeDatabase(string databaseName) 
        {
            throw EntityUtil.NotSupported();
        }
 
        /// 
        /// Begins a database transaction 
        ///  
        /// An object representing the new transaction
        public new EntityTransaction BeginTransaction() 
        {
            return base.BeginTransaction() as EntityTransaction;
        }
 
        /// 
        /// Begins a database transaction 
        ///  
        /// The isolation level of the transaction
        /// An object representing the new transaction 
        public new EntityTransaction BeginTransaction(IsolationLevel isolationLevel)
        {
            return base.BeginTransaction(isolationLevel) as EntityTransaction;
        } 

        ///  
        /// Begins a database transaction 
        /// 
        /// The isolation level of the transaction 
        /// An object representing the new transaction
        protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel)
        {
            if (CurrentTransaction != null) 
            {
                throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_TransactionAlreadyStarted); 
            } 

            if (this._storeConnection == null) 
                throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_ConnectionStringNeededBeforeOperation);

            if (this.State != ConnectionState.Open)
            { 
                throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_ConnectionNotOpen);
            } 
 
            DbTransaction storeTransaction = null;
            try 
            {
                storeTransaction = this._storeConnection.BeginTransaction(isolationLevel);
            }
            catch (Exception e) 
            {
                if (EntityUtil.IsCatchableExceptionType(e)) 
                { 
                    throw EntityUtil.ProviderExceptionWithMessage(
                                            System.Data.Entity.Strings.EntityClient_ErrorInBeginningTransaction, 
                                            e
                                        );
                }
                throw; 
            }
 
            // The provider is problematic if it succeeded in beginning a transaction but returned a null 
            // for the transaction object
            if (storeTransaction == null) 
            {
                throw EntityUtil.ProviderIncompatible(System.Data.Entity.Strings.EntityClient_ReturnedNullOnProviderMethod("BeginTransaction", _storeConnection.GetType().Name));
            }
 
            _currentTransaction = new EntityTransaction(this, storeTransaction);
            return _currentTransaction; 
        } 

        ///  
        /// Enlist in the given transaction
        /// 
        /// The transaction object to enlist into
        public override void EnlistTransaction(Transaction transaction) 
        {
            if (_storeConnection == null) 
                throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_ConnectionStringNeededBeforeOperation); 

            if (this.State != ConnectionState.Open) 
            {
                throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_ConnectionNotOpen);
            }
 
            try
            { 
                _storeConnection.EnlistTransaction(transaction); 
            }
            catch (Exception e) 
            {
                if (EntityUtil.IsCatchableExceptionType(e))
                {
                    throw EntityUtil.Provider(@"EnlistTransaction", e); 
                }
                throw; 
            } 
        }
 
        /// 
        /// Cleans up this connection object
        /// 
        /// true to release both managed and unmanaged resources; false to release only unmanaged resources 
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed", MessageId = "_currentTransaction")]
        [ResourceExposure(ResourceScope.None)] //We are not exposing any resource 
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] //For ChangeConnectionString method call. But since the connection string we pass in is an Empty String, 
                                                     //we consume the resource and do not expose it any further.
        protected override void Dispose(bool disposing) 
        {
            ClearCurrentTransaction();
            bool raiseStateChangeEvent = EntityCloseHelper(false, this.State);
 
            if (disposing)
            { 
                if (this._storeConnection != null) 
                {
                    StoreCloseHelper(); // closes store connection 
                    if (this._storeConnection != null)
                    {
                        if (!this._userOwnsStoreConnection)  // only dispose it if we didn't get it from the user...
                        { 
                            this._storeConnection.Dispose();
                        } 
                        this._storeConnection = null; 
                    }
                } 
            }

            // Change the connection string to just an empty string, ChangeConnectionString should always succeed here,
            // it's unnecessary to pass in the connection string parameter name in the second argument, which we don't 
            // have anyway
            this.ChangeConnectionString(String.Empty); 
 
            if (raiseStateChangeEvent)  // we need to raise the event explicitly
            { 
                OnStateChange(System.Data.ProviderBase.DbConnectionInternal.StateChangeClosed);
            }

            base.Dispose(disposing); 
        }
 
        ///  
        /// Reinitialize this connection object to use the new connection string
        ///  
        /// The new connection string
        [ResourceExposure(ResourceScope.Machine)] //Exposes the file names which are a Machine resource as part of the connection string
        private void ChangeConnectionString(string newConnectionString)
        { 
            DbConnectionOptions userConnectionOptions = s_emptyConnectionOptions;
            if (!String.IsNullOrEmpty(newConnectionString)) 
            { 
                userConnectionOptions = new DbConnectionOptions(newConnectionString, EntityConnectionStringBuilder.Synonyms, false);
            } 

            DbProviderFactory factory = null;
            DbConnection storeConnection = null;
            DbConnectionOptions effectiveConnectionOptions = userConnectionOptions; 

            if (!userConnectionOptions.IsEmpty) 
            { 
                // Check if we have the named connection, if yes, then use the connection string from the configuration manager settings
                string namedConnection = userConnectionOptions[EntityConnectionStringBuilder.NameParameterName]; 
                if (!string.IsNullOrEmpty(namedConnection))
                {
                    // There cannot be other parameters when the named connection is specified
                    if (1 < userConnectionOptions.Parsetable.Count) 
                    {
                        throw EntityUtil.Argument(System.Data.Entity.Strings.EntityClient_ExtraParametersWithNamedConnection); 
                    } 

                    // Find the named connection from the configuration, then extract the settings 
                    ConnectionStringSettings setting = ConfigurationManager.ConnectionStrings[namedConnection];
                    if (setting == null || setting.ProviderName != EntityConnection.s_entityClientProviderName)
                    {
                        throw EntityUtil.Argument(System.Data.Entity.Strings.EntityClient_InvalidNamedConnection); 
                    }
 
                    effectiveConnectionOptions = new DbConnectionOptions(setting.ConnectionString, EntityConnectionStringBuilder.Synonyms, false); 

                    // Check for a nested Name keyword 
                    string nestedNamedConnection = effectiveConnectionOptions[EntityConnectionStringBuilder.NameParameterName];
                    if (!string.IsNullOrEmpty(nestedNamedConnection))
                    {
                        throw EntityUtil.Argument(System.Data.Entity.Strings.EntityClient_NestedNamedConnection(namedConnection)); 
                    }
                } 
 
                //Validate the connection string has the required Keywords( Provider and Metadata)
                //We trim the values for both the Keywords, so a string value with only spaces will throw an exception 
                //reporting back to the user that the Keyword was missing.
                ValidateValueForTheKeyword(effectiveConnectionOptions, EntityConnectionStringBuilder.MetadataParameterName);

                string providerName = ValidateValueForTheKeyword(effectiveConnectionOptions, EntityConnectionStringBuilder.ProviderParameterName); 
                // Get the correct provider factory
                factory = GetFactory(providerName); 
 
                // Create the underlying provider specific connection and give it the connection string from the DbConnectionOptions object
                storeConnection = GetStoreConnection(factory); 

                try
                {
                    // When the value of 'Provider Connection String' is null, it means it has not been present in the entity connection string at all. 
                    // Providers should still be able handle empty connection strings since those may be explicitly passed by clients.
                    string providerConnectionString = effectiveConnectionOptions[EntityConnectionStringBuilder.ProviderConnectionStringParameterName]; 
                    if (providerConnectionString != null) 
                    {
                        storeConnection.ConnectionString = providerConnectionString; 
                    }
                }
                catch (Exception e)
                { 
                    if (EntityUtil.IsCatchableExceptionType(e))
                    { 
                        throw EntityUtil.Provider(@"ConnectionString", e); 
                    }
                    throw; 
                }
            }

            // This lock is to ensure that the connection string matches with the provider connection and metadata workspace that's being 
            // managed by this EntityConnection, so states in this connection object are not messed up.
            // It's not for security, but just to help reduce user error. 
            lock (_connectionStringLock) 
            {
                // Now we have sufficient information and verified the configuration string is good, use them for this connection object 
                // Failure should not occur from this point to the end of this method
                this._providerFactory = factory;
                this._metadataWorkspace = null;
 
                ClearCurrentTransaction();
 
                ResetStoreConnection(storeConnection, null, false); 

                // Remembers the connection options objects with the connection string set by the user 
                this._userConnectionOptions = userConnectionOptions;
                this._effectiveConnectionOptions = effectiveConnectionOptions;
            }
        } 

        private static string ValidateValueForTheKeyword(DbConnectionOptions effectiveConnectionOptions, 
            string keywordName) 
        {
            string keywordValue = effectiveConnectionOptions[keywordName]; 
            if (!string.IsNullOrEmpty(keywordValue))
                keywordValue = keywordValue.Trim(); // be nice to user, always trim the value

            // Check that we have a non-null and non-empty value for the keyword 
            if (string.IsNullOrEmpty(keywordValue))
            { 
                throw EntityUtil.Argument(System.Data.Entity.Strings.EntityClient_ConnectionStringMissingInfo(keywordName)); 
            }
            return keywordValue; 
        }

        private static EdmItemCollection LoadEdmItemCollection(MetadataWorkspace workspace, MetadataArtifactLoader artifactLoader)
        { 
            // Build a string as the key and look up the MetadataCache for a match
            string edmCacheKey = CreateMetadataCacheKey(artifactLoader.GetOriginalPaths(DataSpace.CSpace), null, null); 
 
            // Check the MetadataCache for an entry with this key
            object entryToken; 
            EdmItemCollection edmItemCollection = MetadataCache.GetOrCreateEdmItemCollection(edmCacheKey,
                                                                                        artifactLoader,
                                                                                        out entryToken);
            workspace.RegisterItemCollection(edmItemCollection); 

            // Adding the edm metadata entry token to the workspace, to make sure that this token remains alive till workspace is alive 
            workspace.AddMetadataEntryToken(entryToken); 

            return edmItemCollection; 
        }

        private static void LoadStoreItemCollections(MetadataWorkspace workspace,
                                                     DbConnection storeConnection, 
                                                     DbProviderFactory factory,
                                                     DbConnectionOptions connectionOptions, 
                                                     EdmItemCollection edmItemCollection, 
                                                     MetadataArtifactLoader artifactLoader)
        { 
            Debug.Assert(workspace.IsItemCollectionAlreadyRegistered(DataSpace.CSpace), "C-Space must be loaded before loading S or C-S space");

            // The provider connection string is optional; if it has not been specified,
            // we pick up the store's connection string. 
            //
            string providerConnectionString = connectionOptions[EntityConnectionStringBuilder.ProviderConnectionStringParameterName]; 
            if (string.IsNullOrEmpty(providerConnectionString) && (storeConnection != null)) 
            {
                providerConnectionString = storeConnection.ConnectionString; 
            }

            // Build a string as the key and look up the MetadataCache for a match
            string storeCacheKey = CreateMetadataCacheKey(artifactLoader.GetOriginalPaths(), 
                                                          connectionOptions[EntityConnectionStringBuilder.ProviderParameterName],
                                                          providerConnectionString); 
 
            // Load store metadata.
            object entryToken; 
            StorageMappingItemCollection mappingCollection =
                MetadataCache.GetOrCreateStoreAndMappingItemCollections(storeCacheKey,
                                                                     artifactLoader,
                                                                     edmItemCollection, 
                                                                     out entryToken);
 
            workspace.RegisterItemCollection(mappingCollection.StoreItemCollection); 
            workspace.RegisterItemCollection(mappingCollection);
 
            // Adding the store metadata entry token to the workspace
            workspace.AddMetadataEntryToken(entryToken);
        }
 
        private static string GetErrorMessageWorthyProviderName(DbProviderFactory factory)
        { 
            EntityUtil.CheckArgumentNull(factory, "factory"); 

            string providerName; 
            if (!EntityUtil.TryGetProviderInvariantName(factory, out providerName))
            {
                providerName = factory.GetType().FullName;
            } 
            return providerName;
        } 
 
        /// 
        /// Create a key to be used with the MetadataCache from a connection options object 
        /// 
        /// A list of metadata file paths
        /// The provider name
        /// The provider connection string 
        /// The key
        private static string CreateMetadataCacheKey(IList paths, string providerName, string providerConnectionString) 
        { 
            int resultCount = 0;
            string result; 

            // Do a first pass to calculate the output size of the metadata cache key,
            // then another pass to populate a StringBuilder with the exact size and
            // get the result. 
            CreateMetadataCacheKeyWithCount(paths, providerName, providerConnectionString,
                false, ref resultCount, out result); 
            CreateMetadataCacheKeyWithCount(paths, providerName, providerConnectionString, 
                true, ref resultCount, out result);
 
            return result;
        }

        ///  
        /// Create a key to be used with the MetadataCache from a connection options
        /// object. 
        ///  
        /// A list of metadata file paths
        /// The provider name 
        /// The provider connection string
        /// Whether the result variable should be built.
        /// 
        /// On entry, the expected size of the result (unused if buildResult is false). 
        /// After execution, the effective result.
        /// The key. 
        ///  
        /// This method should be called once with buildResult=false, to get
        /// the size of the resulting key, and once with buildResult=true 
        /// and the size specification.
        /// 
        private static void CreateMetadataCacheKeyWithCount(IList paths,
            string providerName, string providerConnectionString, 
            bool buildResult, ref int resultCount, out string result)
        { 
            // Build a string as the key and look up the MetadataCache for a match 
            StringBuilder keyString;
            if (buildResult) 
            {
                keyString = new StringBuilder(resultCount);
            }
            else 
            {
                keyString = null; 
            } 

            // At this point, we've already used resultCount. Reset it 
            // to zero to make the final debug assertion that our computation
            // is correct.
            resultCount = 0;
 
            if (!string.IsNullOrEmpty(providerName))
            { 
                resultCount += providerName.Length + 1; 
                if (buildResult)
                { 
                    keyString.Append(providerName);
                    keyString.Append(EntityConnection.s_semicolonSeparator);
                }
            } 

            if (paths != null) 
            { 
                for (int i = 0; i < paths.Count; i++)
                { 
                    if (paths[i].Length > 0)
                    {
                        if (i > 0)
                        { 
                            resultCount++;
                            if (buildResult) 
                            { 
                                keyString.Append(EntityConnection.s_metadataPathSeparator);
                            } 
                        }
                        resultCount += paths[i].Length;
                        if (buildResult)
                        { 
                            keyString.Append(paths[i]);
                        } 
                    } 
                }
                resultCount++; 
                if (buildResult)
                {
                    keyString.Append(EntityConnection.s_semicolonSeparator);
                } 
            }
 
            if (!string.IsNullOrEmpty(providerConnectionString)) 
            {
                resultCount += providerConnectionString.Length; 
                if (buildResult)
                {
                    keyString.Append(providerConnectionString);
                } 
            }
 
            if (buildResult) 
            {
                result = keyString.ToString(); 
            }
            else
            {
                result = null; 
            }
 
            System.Diagnostics.Debug.Assert( 
                !buildResult || (result.Length == resultCount));
        } 

        /// 
        /// Clears the current transaction for this connection
        ///  
        internal void ClearCurrentTransaction()
        { 
            _currentTransaction = null; 
        }
 
        /// 
        /// Helper method invoked as part of Close()/Dispose() that releases the underlying
        /// store connection and raises the appropriate event.
        ///  
        private void CloseHelper()
        { 
            ConnectionState previousState = this.State; // the public connection state before cleanup 
            StoreCloseHelper();
            EntityCloseHelper( 
                            true,   // raise the state change event
                            previousState
                        );
        } 

        ///  
        /// Store-specific helper method invoked as part of Close()/Dispose(). 
        /// 
        private void StoreCloseHelper() 
        {
            try
            {
                if (this._storeConnection != null && (this._storeConnection.State != ConnectionState.Closed)) 
                {
                    this._storeConnection.Close(); 
                } 
                // Need to disassociate the transaction object with this connection
                ClearCurrentTransaction(); 
            }
            catch (Exception e)
            {
                if (EntityUtil.IsCatchableExceptionType(e)) 
                {
                    throw EntityUtil.ProviderExceptionWithMessage( 
                                        System.Data.Entity.Strings.EntityClient_ErrorInClosingConnection, 
                                        e
                                    ); 
                }
                throw;
            }
        } 

        ///  
        /// Entity-specific helper method invoked as part of Close()/Dispose(). 
        /// 
        /// Indicates whether we need to raise the state change event here 
        /// The public state of the connection before cleanup began
        /// true if the caller needs to raise the state change event
        private bool EntityCloseHelper(bool fireEventOnStateChange, ConnectionState previousState)
        { 
            bool result = false;
 
            this._entityClientConnectionState = ConnectionState.Closed; 

            if (previousState == ConnectionState.Open) 
            {
                if (fireEventOnStateChange)
                {
                    OnStateChange(System.Data.ProviderBase.DbConnectionInternal.StateChangeClosed); 
                }
                else 
                { 
                    result = true;  // we didn't raise the event here; the caller should do that
                } 
            }

            return result;
        } 

        ///  
        /// Call to determine if changes to the entity object are currently permitted. 
        /// 
        private void ValidateChangesPermitted() 
        {
            if (_initialized)
            {
                throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_SettingsCannotBeChangedOnOpenConnection); 
            }
        } 
 
        /// 
        /// Returns the DbProviderFactory associated with specified provider string 
        /// 
        private DbProviderFactory GetFactory(string providerString)
        {
            try 
            {
                return DbProviderFactories.GetFactory(providerString); 
            } 
            catch (ArgumentException e)
            { 
                throw EntityUtil.Argument(System.Data.Entity.Strings.EntityClient_InvalidStoreProvider, e);
            }
        }
 
        /// 
        /// Uses DbProviderFactory to create a DbConnection 
        ///  
        private DbConnection GetStoreConnection(DbProviderFactory factory)
        { 
            DbConnection storeConnection = factory.CreateConnection();
            if (storeConnection == null)
            {
                throw EntityUtil.ProviderIncompatible(System.Data.Entity.Strings.EntityClient_ReturnedNullOnProviderMethod("CreateConnection", factory.GetType().Name)); 
            }
            return storeConnection; 
        } 

    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK