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

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- FunctionUpdateCommand.cs
- WeakReadOnlyCollection.cs
- DynamicMetaObjectBinder.cs
- ConfigurationManagerHelperFactory.cs
- RawAppCommandInputReport.cs
- RoleManagerSection.cs
- Timeline.cs
- UdpAnnouncementEndpoint.cs
- X509RawDataKeyIdentifierClause.cs
- Logging.cs
- SchemaImporter.cs
- IIS7WorkerRequest.cs
- DataControlImageButton.cs
- SystemUdpStatistics.cs
- Int64.cs
- Int32KeyFrameCollection.cs
- LogReserveAndAppendState.cs
- CompiledQueryCacheKey.cs
- AppendHelper.cs
- SimpleTextLine.cs
- ObjectPropertyMapping.cs
- XmlText.cs
- MapPathBasedVirtualPathProvider.cs
- CfgRule.cs
- DataGridViewRowDividerDoubleClickEventArgs.cs
- DataGridViewRowEventArgs.cs
- VirtualDirectoryMapping.cs
- TraceInternal.cs
- WinFormsComponentEditor.cs
- DirectoryNotFoundException.cs
- Rotation3DAnimationBase.cs
- MiniAssembly.cs
- TreeNodeCollection.cs
- HttpResponse.cs
- GroupItemAutomationPeer.cs
- SourceFileBuildProvider.cs
- ColumnWidthChangingEvent.cs
- SqlNode.cs
- ConfigXmlComment.cs
- DataGridColumnCollection.cs
- diagnosticsswitches.cs
- FieldMetadata.cs
- DescendantOverDescendantQuery.cs
- SupportedAddressingMode.cs
- SqlReorderer.cs
- SettingsPropertyWrongTypeException.cs
- PropertyInformation.cs
- SemaphoreFullException.cs
- DSASignatureFormatter.cs
- ResourceDictionaryCollection.cs
- Thread.cs
- EntityKeyElement.cs
- Renderer.cs
- ToolBar.cs
- CanExecuteRoutedEventArgs.cs
- CompilerGlobalScopeAttribute.cs
- CombinedGeometry.cs
- AdCreatedEventArgs.cs
- RectangleConverter.cs
- IMembershipProvider.cs
- InstanceData.cs
- RegexStringValidator.cs
- DataBinding.cs
- XmlEncoding.cs
- SocketElement.cs
- Publisher.cs
- DLinqTableProvider.cs
- CodeTypeParameterCollection.cs
- Validator.cs
- XmlSchemaObjectCollection.cs
- AccessKeyManager.cs
- DataGridViewTextBoxCell.cs
- LoginDesignerUtil.cs
- BooleanSwitch.cs
- FrameworkRichTextComposition.cs
- METAHEADER.cs
- CharacterBufferReference.cs
- Panel.cs
- PageBuildProvider.cs
- StsCommunicationException.cs
- DeclarativeExpressionConditionDeclaration.cs
- ProcessThread.cs
- Condition.cs
- DataContractSet.cs
- TemplateContainer.cs
- DecryptedHeader.cs
- SqlDataRecord.cs
- WebRequestModuleElement.cs
- SqlConnectionStringBuilder.cs
- Point3DIndependentAnimationStorage.cs
- RenderContext.cs
- PasswordBoxAutomationPeer.cs
- Deserializer.cs
- SchemaNotation.cs
- QueryParameter.cs
- PerCallInstanceContextProvider.cs
- ExclusiveNamedPipeTransportManager.cs
- ValidatorCollection.cs
- ContainerVisual.cs
- CodeCommentStatement.cs