Fix database debugging tools

This commit is contained in:
Stephan
2018-05-30 16:40:25 +02:00
parent 632e401d33
commit 825f785a2b
4 changed files with 65 additions and 62 deletions

View File

@@ -3,9 +3,11 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data; using System.Data;
using System.Data.Common; using System.Data.Common;
using System.Data.SqlServerCe;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using Umbraco.Core.Persistence.FaultHandling;
namespace Umbraco.Core.Persistence namespace Umbraco.Core.Persistence
{ {
@@ -29,11 +31,7 @@ namespace Umbraco.Core.Persistence
{ {
lock (Commands) lock (Commands)
{ {
var tuple = Commands.FirstOrDefault(x => var tuple = Commands.FirstOrDefault(x => x.Item2.TryGetTarget(out var c) && c == command);
{
IDbCommand c;
return x.Item2.TryGetTarget(out c) && c == command;
});
return tuple == null ? "?" : tuple.Item1; return tuple == null ? "?" : tuple.Item1;
} }
} }
@@ -42,28 +40,30 @@ namespace Umbraco.Core.Persistence
{ {
con = con.UnwrapUmbraco(); con = con.UnwrapUmbraco();
var ceCon = con as System.Data.SqlServerCe.SqlCeConnection; if (con is SqlCeConnection) return null; // "NotSupported: SqlCE";
if (ceCon != null) return null; // "NotSupported: SqlCE";
var dbCon = con as DbConnection; return con is DbConnection dbCon
return dbCon == null ? GetReferencedObjects(dbCon)
? "NotSupported: " + con.GetType() : "NotSupported: " + con.GetType();
: GetReferencedObjects(dbCon);
} }
public static string GetReferencedObjects(DbConnection con) public static string GetReferencedObjects(DbConnection con)
{ {
var t = con.GetType(); if (con.UnwrapUmbraco() is DbConnection ucon && !(ucon is SqlCeConnection))
con = ucon;
else
return "NotSupported: " + con.GetType();
var t = con.GetType();
var field = t.GetField("_innerConnection", BindingFlags.Instance | BindingFlags.NonPublic); var field = t.GetField("_innerConnection", BindingFlags.Instance | BindingFlags.NonPublic);
if (field == null) throw new Exception("panic: _innerConnection (" + t + ")."); if (field == null) throw new Exception("panic: _innerConnection (" + t + ").");
var innerConnection = field.GetValue(con); var innerConnection = field.GetValue(con);
var tin = innerConnection.GetType();
var tin = innerConnection.GetType();
var fi = con is System.Data.SqlClient.SqlConnection var fi = con is System.Data.SqlClient.SqlConnection
? tin.BaseType.BaseType.GetField("_referenceCollection", BindingFlags.Instance | BindingFlags.NonPublic) ? tin.BaseType?.BaseType?.GetField("_referenceCollection", BindingFlags.Instance | BindingFlags.NonPublic)
: tin.BaseType.GetField("_referenceCollection", BindingFlags.Instance | BindingFlags.NonPublic); : tin.BaseType?.GetField("_referenceCollection", BindingFlags.Instance | BindingFlags.NonPublic);
if (fi == null) if (fi == null)
//return ""; //return "";
throw new Exception("panic: referenceCollection."); throw new Exception("panic: referenceCollection.");
@@ -73,7 +73,7 @@ namespace Umbraco.Core.Persistence
//return ""; //return "";
throw new Exception("panic: innerCollection."); throw new Exception("panic: innerCollection.");
field = rc.GetType().BaseType.GetField("_items", BindingFlags.Instance | BindingFlags.NonPublic); field = rc.GetType().BaseType?.GetField("_items", BindingFlags.Instance | BindingFlags.NonPublic);
if (field == null) throw new Exception("panic: items."); if (field == null) throw new Exception("panic: items.");
var items = field.GetValue(rc); var items = field.GetValue(rc);
var prop = items.GetType().GetProperty("Length", BindingFlags.Instance | BindingFlags.Public); var prop = items.GetType().GetProperty("Length", BindingFlags.Instance | BindingFlags.Public);
@@ -116,21 +116,26 @@ namespace Umbraco.Core.Persistence
i, inUse, objTarget.GetType(), objTarget.GetHashCode()); i, inUse, objTarget.GetType(), objTarget.GetHashCode());
DbCommand cmd = null; DbCommand cmd = null;
if (objTarget is DbDataReader) switch (objTarget)
{ {
//var rdr = objTarget as DbDataReader; case DbDataReader _:
try //var rdr = objTarget as DbDataReader;
{ try
cmd = objTarget.GetType().GetProperty("Command", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(objTarget, null) as DbCommand; {
} var commandProp = objTarget.GetType().GetProperty("Command", BindingFlags.Instance | BindingFlags.NonPublic);
catch (Exception e) if (commandProp == null)
{ throw new Exception($"panic: failed to get Command property of {objTarget.GetType().FullName}.");
result.AppendFormat("\t\tObjTarget: DbDataReader, Exception: {0}" + Environment.NewLine, e); cmd = commandProp.GetValue(objTarget, null) as DbCommand;
} }
} catch (Exception e)
else if (objTarget is DbCommand) {
{ result.AppendFormat("\t\tObjTarget: DbDataReader, Exception: {0}" + Environment.NewLine, e);
cmd = objTarget as DbCommand; }
break;
case DbCommand command:
cmd = command;
break;
} }
if (cmd == null) if (cmd == null)
{ {
@@ -152,7 +157,9 @@ namespace Umbraco.Core.Persistence
if (pi.PropertyType != typeof (DbConnection) || pi.Name != "Connection") continue; if (pi.PropertyType != typeof (DbConnection) || pi.Name != "Connection") continue;
var con1 = pi.GetValue(cmd, null) as DbConnection; var con0 = pi.GetValue(cmd, null);
if (!(con0 is DbConnection con1))
throw new Exception($"panic: expected DbConnection, got {con0?.GetType().FullName ?? "null"}.");
result.AppendFormat("\t\t\tConnection type=\"{0}\" state=\"{1}\" hashCode=\"{2}\"" + Environment.NewLine, result.AppendFormat("\t\t\tConnection type=\"{0}\" state=\"{1}\" hashCode=\"{2}\"" + Environment.NewLine,
con1.GetType(), con1.State, con1.GetHashCode()); con1.GetType(), con1.State, con1.GetHashCode());

View File

@@ -5,6 +5,7 @@ using System.Data.SqlClient;
using System.Data.SqlServerCe; using System.Data.SqlServerCe;
using System.Linq; using System.Linq;
using MySql.Data.MySqlClient; using MySql.Data.MySqlClient;
using StackExchange.Profiling.Data;
using Umbraco.Core.Composing; using Umbraco.Core.Composing;
using Umbraco.Core.Logging; using Umbraco.Core.Logging;
using Umbraco.Core.Persistence.FaultHandling; using Umbraco.Core.Persistence.FaultHandling;
@@ -78,18 +79,13 @@ namespace Umbraco.Core.Persistence
/// OnConnectionOpened); this unwraps and returns the original database connection.</remarks> /// OnConnectionOpened); this unwraps and returns the original database connection.</remarks>
internal static IDbConnection UnwrapUmbraco(this IDbConnection connection) internal static IDbConnection UnwrapUmbraco(this IDbConnection connection)
{ {
IDbConnection unwrapped; var unwrapped = connection;
IDbConnection c;
var c = connection;
do do
{ {
unwrapped = c; c = unwrapped;
if (unwrapped is ProfiledDbConnection profiled) unwrapped = profiled.InnerConnection;
var profiled = unwrapped as StackExchange.Profiling.Data.ProfiledDbConnection; if (unwrapped is RetryDbConnection retrying) unwrapped = retrying.Inner;
if (profiled != null) unwrapped = profiled.InnerConnection;
var retrying = unwrapped as RetryDbConnection;
if (retrying != null) unwrapped = retrying.Inner;
} while (c != unwrapped); } while (c != unwrapped);
@@ -100,22 +96,23 @@ namespace Umbraco.Core.Persistence
{ {
try try
{ {
if (connection is SqlConnection) switch (connection)
{ {
var builder = new SqlConnectionStringBuilder(connection.ConnectionString); case SqlConnection _:
return $"DataSource: {builder.DataSource}, InitialCatalog: {builder.InitialCatalog}"; {
} var builder = new SqlConnectionStringBuilder(connection.ConnectionString);
return $"DataSource: {builder.DataSource}, InitialCatalog: {builder.InitialCatalog}";
if (connection is SqlCeConnection) }
{ case SqlCeConnection _:
var builder = new SqlCeConnectionStringBuilder(connection.ConnectionString); {
return $"DataSource: {builder.DataSource}"; var builder = new SqlCeConnectionStringBuilder(connection.ConnectionString);
} return $"DataSource: {builder.DataSource}";
}
if (connection is MySqlConnection) case MySqlConnection _:
{ {
var builder = new MySqlConnectionStringBuilder(connection.ConnectionString); var builder = new MySqlConnectionStringBuilder(connection.ConnectionString);
return $"Server: {builder.Server}, Database: {builder.Database}"; return $"Server: {builder.Server}, Database: {builder.Database}";
}
} }
} }
catch (Exception ex) catch (Exception ex)

View File

@@ -177,8 +177,7 @@ namespace Umbraco.Core.Persistence.FaultHandling
set set
{ {
if (value == null) throw new ArgumentNullException(nameof(value)); if (value == null) throw new ArgumentNullException(nameof(value));
var connection = value as RetryDbConnection; if (!(value is RetryDbConnection connection)) throw new ArgumentException("Value is not a FaultHandlingDbConnection instance.");
if (connection == null) throw new ArgumentException("Value is not a FaultHandlingDbConnection instance.");
if (_connection != null && _connection != connection) throw new Exception("Value is another FaultHandlingDbConnection instance."); if (_connection != null && _connection != connection) throw new Exception("Value is another FaultHandlingDbConnection instance.");
_connection = connection; _connection = connection;
_inner.Connection = connection.Inner; _inner.Connection = connection.Inner;

View File

@@ -149,7 +149,7 @@ namespace Umbraco.Core.Persistence
#if DEBUG_DATABASES #if DEBUG_DATABASES
// determines the database connection SPID for debugging // determines the database connection SPID for debugging
if (DatabaseType == DBType.MySql) if (DatabaseType.IsMySql())
{ {
using (var command = connection.CreateCommand()) using (var command = connection.CreateCommand())
{ {
@@ -157,7 +157,7 @@ namespace Umbraco.Core.Persistence
_spid = Convert.ToInt32(command.ExecuteScalar()); _spid = Convert.ToInt32(command.ExecuteScalar());
} }
} }
else if (DatabaseType == DBType.SqlServer) else if (DatabaseType.IsSqlServer())
{ {
using (var command = connection.CreateCommand()) using (var command = connection.CreateCommand())
{ {
@@ -183,7 +183,7 @@ namespace Umbraco.Core.Persistence
} }
#if DEBUG_DATABASES #if DEBUG_DATABASES
public override void OnConnectionClosing(IDbConnection conn) protected override void OnConnectionClosing(DbConnection conn)
{ {
_spid = -1; _spid = -1;
base.OnConnectionClosing(conn); base.OnConnectionClosing(conn);
@@ -212,7 +212,7 @@ namespace Umbraco.Core.Persistence
#if DEBUG_DATABASES #if DEBUG_DATABASES
// detects whether the command is already in use (eg still has an open reader...) // detects whether the command is already in use (eg still has an open reader...)
DatabaseDebugHelper.SetCommand(cmd, InstanceId + " [T" + Thread.CurrentThread.ManagedThreadId + "]"); DatabaseDebugHelper.SetCommand(cmd, InstanceId + " [T" + System.Threading.Thread.CurrentThread.ManagedThreadId + "]");
var refsobj = DatabaseDebugHelper.GetReferencedObjects(cmd.Connection); var refsobj = DatabaseDebugHelper.GetReferencedObjects(cmd.Connection);
if (refsobj != null) _logger.Debug<UmbracoDatabase>("Oops!" + Environment.NewLine + refsobj); if (refsobj != null) _logger.Debug<UmbracoDatabase>("Oops!" + Environment.NewLine + refsobj);
#endif #endif