在Open上包装实体框架异常

时间:2015-04-14 19:31:29

标签: c# entity-framework

如果运行我的服务的用户无权访问数据库,则EF将失败并出现以下异常:

  

System.Data.Entity.Core.ProviderIncompatibleException:从数据库获取提供程序信息时发生错误。这可能是由实体框架使用不正确的连接字符串引起的。检查内部异常以获取详细信息,并确保连接字符串正确。

这是内部异常(具有特定细节):

System.Data.Entity.Core.ProviderIncompatibleException: The provider did not return a ProviderManifestToken string. ---> System.Data.SqlClient.SqlException: Login failed for user '[redacted]'.
   at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
   at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
   at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
   at System.Data.SqlClient.SqlInternalConnectionTds.CompleteLogin(Boolean enlistOK)
   at System.Data.SqlClient.SqlInternalConnectionTds.AttemptOneLogin(ServerInfo serverInfo, String newPassword, SecureString newSecurePassword, Boolean ignoreSniOpenTimeout, TimeoutTimer timeout, Boolean withFailover)
   at System.Data.SqlClient.SqlInternalConnectionTds.LoginNoFailover(ServerInfo serverInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString connectionOptions, SqlCredential credential, TimeoutTimer timeout)
   at System.Data.SqlClient.SqlInternalConnectionTds.OpenLoginEnlist(TimeoutTimer timeout, SqlConnectionString connectionOptions, SqlCredential credential, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance)
   at System.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString connectionOptions, SqlCredential credential, Object providerInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString userConnectionOptions, SessionData reconnectSessionData)
   at System.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, DbConnectionOptions userOptions)
   at System.Data.ProviderBase.DbConnectionFactory.CreatePooledConnection(DbConnectionPool pool, DbConnection owningObject, DbConnectionOptions options, DbConnectionPoolKey poolKey, DbConnectionOptions userOptions)
   at System.Data.ProviderBase.DbConnectionPool.CreateObject(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
   at System.Data.ProviderBase.DbConnectionPool.UserCreateRequest(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
   at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection)
   at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal& connection)
   at System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection)
   at System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
   at System.Data.SqlClient.SqlConnection.TryOpenInner(TaskCompletionSource`1 retry)
   at System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry)
   at System.Data.SqlClient.SqlConnection.Open()
   at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.<>c__DisplayClass1.<Execute>b__0()
   at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func`1 operation)
   at System.Data.Entity.SqlServer.SqlProviderServices.UsingConnection(DbConnection sqlConnection, Action`1 act)
   at System.Data.Entity.SqlServer.SqlProviderServices.UsingMasterConnection(DbConnection sqlConnection, Action`1 act)
   at System.Data.Entity.SqlServer.SqlProviderServices.GetDbProviderManifestToken(DbConnection connection)
   at System.Data.Entity.Core.Common.DbProviderServices.GetProviderManifestToken(DbConnection connection)
   --- End of inner exception stack trace ---
   at System.Data.Entity.Core.Common.DbProviderServices.GetProviderManifestToken(DbConnection connection)
   at System.Data.Entity.Utilities.DbProviderServicesExtensions.GetProviderManifestTokenChecked(DbProviderServices providerServices, DbConnection connection)

问题本身非常明确:用户无权访问。但是,不是清楚的是,我们正在尝试打开哪个数据库。当我在一个调用的范围内使用多个数据库(几个DbContexts)时,我想知道哪个数据库拒绝我的用户,我想快速,轻松地知道它。

因此,我正在寻找一些方法来覆盖连接的开放,这样我就可以捕获异常并用更多信息包装它。但是,我没有看到一种简单的方法,因为DbContext只使用DbConnection。如何挂钩此过程以包装连接开口?或者有更好的方法来获取这些信息而不是我正在尝试做的事情吗?

异常本身并没有给我一个有用的堆栈跟踪,因为我的服务完全是async

2 个答案:

答案 0 :(得分:2)

内部异常包含您可能需要的大部分细节。在这种情况下,基本异常是SqlException

try
{
    //code that fires the error
}
catch (ProviderIncompatibleException ex)
{
    SqlException baseException = ex.GetBaseException() as SqlException;
    Console.WriteLine(baseException.Server);
}

您可以在捕获异常的任何地方记录此附加信息。

答案 1 :(得分:0)

  public class MyContext : DbContext
    {
        public MyContext() : base(new MyDbConnection(), true) {

        }
    }

    public class MyDbConnection : DbConnection
    {
        private SqlConnection connection;

        public MyDbConnection() {
            // init connection here
        }

        protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel) {
            return connection.BeginTransaction();
        }

        public override void Close() {
            connection.Close();
        }

        public override void ChangeDatabase(string databaseName) {
            connection.ChangeDatabase(databaseName);
        }

        public override void Open() {
            try {
                connection.Open();
            }
            catch (Exception ex) {
                throw new MyCustomException(this.ConnectionString, ex);
            }
        }

        public override string ConnectionString {
            get {
                return this.connection.ConnectionString;
            }
            set {
                this.connection.ConnectionString = value;
            }
        }

        public override string Database {
            get {
                return this.connection.Database;
            }
        }

        public override ConnectionState State {
            get {
                return this.connection.State;
            }
        }

        public override string DataSource {
            get {
                return this.connection.DataSource;
            }
        }

        public override string ServerVersion {
            get {
                return this.connection.ServerVersion;
            }
        }

        protected override DbCommand CreateDbCommand() {
            return this.connection.CreateCommand();
        }

}

你明白了:)祝你好运