2012-12-15 08:41:46 +05:00
|
|
|
using System;
|
2012-12-12 03:47:04 +05:00
|
|
|
using System.Data;
|
|
|
|
|
using System.Data.Common;
|
2016-04-20 12:50:52 +02:00
|
|
|
using System.Text;
|
2016-04-12 15:11:07 +02:00
|
|
|
using NPoco;
|
2013-05-10 10:15:30 -02:00
|
|
|
using StackExchange.Profiling;
|
2013-01-18 09:00:18 -01:00
|
|
|
using Umbraco.Core.Logging;
|
2016-04-12 15:11:07 +02:00
|
|
|
using Umbraco.Core.Persistence.FaultHandling;
|
2016-04-12 19:55:50 +02:00
|
|
|
using Umbraco.Core.Persistence.SqlSyntax;
|
2012-12-12 03:47:04 +05:00
|
|
|
|
|
|
|
|
namespace Umbraco.Core.Persistence
|
|
|
|
|
{
|
2015-01-09 15:27:47 +11:00
|
|
|
/// <summary>
|
2016-04-12 15:11:07 +02:00
|
|
|
/// Extends NPoco Database for Umbraco.
|
2015-01-09 15:27:47 +11:00
|
|
|
/// </summary>
|
|
|
|
|
/// <remarks>
|
2016-04-12 15:11:07 +02:00
|
|
|
/// <para>Is used everywhere in place of the original NPoco Database object, and provides additional features
|
|
|
|
|
/// such as profiling, retry policies, logging, etc.</para>
|
|
|
|
|
/// <para>Is never created directly but obtained from the <see cref="DefaultDatabaseFactory"/>.</para>
|
2016-04-12 19:55:50 +02:00
|
|
|
/// <para>It implements IDisposeOnRequestEnd which means it will be disposed when the request ends, which
|
|
|
|
|
/// automatically closes the connection - as implemented by NPoco Database.Dispose().</para>
|
2015-01-09 15:27:47 +11:00
|
|
|
/// </remarks>
|
2016-04-12 19:55:50 +02:00
|
|
|
public class UmbracoDatabase : Database, IDisposeOnRequestEnd, IUmbracoDatabaseConfig
|
2015-01-09 15:27:47 +11:00
|
|
|
{
|
2016-04-12 15:11:07 +02:00
|
|
|
// Umbraco's default isolation level is RepeatableRead
|
|
|
|
|
private const IsolationLevel DefaultIsolationLevel = IsolationLevel.RepeatableRead;
|
|
|
|
|
|
2015-01-09 15:27:47 +11:00
|
|
|
private readonly ILogger _logger;
|
2016-04-12 15:11:07 +02:00
|
|
|
private readonly RetryPolicy _connectionRetryPolicy;
|
|
|
|
|
private readonly RetryPolicy _commandRetryPolicy;
|
2015-07-22 12:10:21 +02:00
|
|
|
private bool _enableCount;
|
|
|
|
|
|
2015-01-09 15:27:47 +11:00
|
|
|
/// <summary>
|
|
|
|
|
/// Used for testing
|
|
|
|
|
/// </summary>
|
2016-04-12 19:55:50 +02:00
|
|
|
internal Guid InstanceId { get; } = Guid.NewGuid();
|
|
|
|
|
|
|
|
|
|
public ISqlSyntaxProvider SqlSyntax { get; }
|
2015-01-09 15:27:47 +11:00
|
|
|
|
2015-07-22 12:10:21 +02:00
|
|
|
/// <summary>
|
|
|
|
|
/// Generally used for testing, will output all SQL statements executed to the logger
|
|
|
|
|
/// </summary>
|
|
|
|
|
internal bool EnableSqlTrace { get; set; }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Used for testing
|
|
|
|
|
/// </summary>
|
|
|
|
|
internal void EnableSqlCount()
|
|
|
|
|
{
|
|
|
|
|
_enableCount = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Used for testing
|
|
|
|
|
/// </summary>
|
|
|
|
|
internal void DisableSqlCount()
|
|
|
|
|
{
|
|
|
|
|
_enableCount = false;
|
|
|
|
|
SqlCount = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Used for testing
|
|
|
|
|
/// </summary>
|
|
|
|
|
internal int SqlCount { get; private set; }
|
|
|
|
|
|
2016-04-12 15:11:07 +02:00
|
|
|
// used by DefaultDatabaseFactory
|
|
|
|
|
// creates one instance per request
|
|
|
|
|
// also used by DatabaseContext for creating DBs and upgrading
|
2016-04-12 19:55:50 +02:00
|
|
|
public UmbracoDatabase(string connectionString,
|
|
|
|
|
ISqlSyntaxProvider sqlSyntax, DatabaseType databaseType, DbProviderFactory provider,
|
|
|
|
|
ILogger logger,
|
|
|
|
|
RetryPolicy connectionRetryPolicy = null, RetryPolicy commandRetryPolicy = null)
|
|
|
|
|
: base(connectionString, databaseType, provider, DefaultIsolationLevel)
|
2015-01-09 15:27:47 +11:00
|
|
|
{
|
2016-04-12 19:55:50 +02:00
|
|
|
SqlSyntax = sqlSyntax;
|
2015-01-09 15:27:47 +11:00
|
|
|
_logger = logger;
|
2016-04-12 15:11:07 +02:00
|
|
|
_connectionRetryPolicy = connectionRetryPolicy;
|
|
|
|
|
_commandRetryPolicy = commandRetryPolicy;
|
2015-07-22 12:10:21 +02:00
|
|
|
EnableSqlTrace = false;
|
2015-01-09 15:27:47 +11:00
|
|
|
}
|
|
|
|
|
|
2016-04-14 18:17:18 +02:00
|
|
|
// INTERNAL FOR UNIT TESTS
|
|
|
|
|
internal UmbracoDatabase(DbConnection connection,
|
2016-07-01 09:09:50 +02:00
|
|
|
ISqlSyntaxProvider sqlSyntax, DatabaseType databaseType,
|
2016-04-14 18:17:18 +02:00
|
|
|
ILogger logger)
|
2016-07-01 09:09:50 +02:00
|
|
|
: base(connection, databaseType, DefaultIsolationLevel)
|
2016-04-14 18:17:18 +02:00
|
|
|
{
|
|
|
|
|
SqlSyntax = sqlSyntax;
|
|
|
|
|
_logger = logger;
|
|
|
|
|
EnableSqlTrace = false;
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-18 14:58:20 +02:00
|
|
|
// fixme: these two could be an extension method of IUmbracoDatabaseConfig
|
2016-04-12 19:55:50 +02:00
|
|
|
public Sql<SqlContext> Sql()
|
2015-01-09 15:27:47 +11:00
|
|
|
{
|
2016-04-12 19:55:50 +02:00
|
|
|
return NPoco.Sql.BuilderFor(new SqlContext(this));
|
2015-01-09 15:27:47 +11:00
|
|
|
}
|
|
|
|
|
|
2016-07-18 14:58:20 +02:00
|
|
|
public Sql<SqlContext> Sql(string sql, params object[] args)
|
|
|
|
|
{
|
|
|
|
|
return NPoco.Sql.BuilderFor(new SqlContext(this)).Append(sql, args);
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-12 15:11:07 +02:00
|
|
|
protected override DbConnection OnConnectionOpened(DbConnection connection)
|
2015-01-09 15:27:47 +11:00
|
|
|
{
|
2016-04-12 19:55:50 +02:00
|
|
|
if (connection == null) throw new ArgumentNullException(nameof(connection));
|
2015-01-09 15:27:47 +11:00
|
|
|
|
2016-04-12 15:11:07 +02:00
|
|
|
// wrap the connection with a profiling connection that tracks timings
|
|
|
|
|
connection = new StackExchange.Profiling.Data.ProfiledDbConnection(connection, MiniProfiler.Current);
|
2013-01-18 09:00:18 -01:00
|
|
|
|
2016-04-12 15:11:07 +02:00
|
|
|
// wrap the connection with a retrying connection
|
|
|
|
|
if (_connectionRetryPolicy != null || _commandRetryPolicy != null)
|
|
|
|
|
connection = new RetryDbConnection(connection, _connectionRetryPolicy, _commandRetryPolicy);
|
2016-02-04 14:51:19 +01:00
|
|
|
|
2016-04-12 15:11:07 +02:00
|
|
|
return connection;
|
2013-05-10 10:15:30 -02:00
|
|
|
}
|
2015-01-09 15:27:47 +11:00
|
|
|
|
2016-04-12 15:11:07 +02:00
|
|
|
protected override void OnException(Exception x)
|
2013-01-18 09:00:18 -01:00
|
|
|
{
|
2015-10-30 11:24:18 +13:00
|
|
|
_logger.Error<UmbracoDatabase>("Database exception occurred", x);
|
2013-01-18 09:00:18 -01:00
|
|
|
base.OnException(x);
|
|
|
|
|
}
|
2015-07-22 12:10:21 +02:00
|
|
|
|
2016-04-12 15:11:07 +02:00
|
|
|
// fixme.poco - has new interceptors?
|
|
|
|
|
|
|
|
|
|
protected override void OnExecutingCommand(DbCommand cmd)
|
2016-02-04 14:51:19 +01:00
|
|
|
{
|
|
|
|
|
// if no timeout is specified, and the connection has a longer timeout, use it
|
|
|
|
|
if (OneTimeCommandTimeout == 0 && CommandTimeout == 0 && cmd.Connection.ConnectionTimeout > 30)
|
|
|
|
|
cmd.CommandTimeout = cmd.Connection.ConnectionTimeout;
|
2016-04-20 12:50:52 +02:00
|
|
|
|
|
|
|
|
if (EnableSqlTrace)
|
|
|
|
|
{
|
|
|
|
|
var sb = new StringBuilder();
|
|
|
|
|
sb.Append(cmd.CommandText);
|
|
|
|
|
foreach (DbParameter p in cmd.Parameters)
|
|
|
|
|
{
|
|
|
|
|
sb.Append(" - ");
|
|
|
|
|
sb.Append(p.Value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_logger.Debug<UmbracoDatabase>(sb.ToString());
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-04 14:51:19 +01:00
|
|
|
base.OnExecutingCommand(cmd);
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-12 15:11:07 +02:00
|
|
|
protected override void OnExecutedCommand(DbCommand cmd)
|
2015-07-22 12:10:21 +02:00
|
|
|
{
|
|
|
|
|
if (_enableCount)
|
|
|
|
|
{
|
|
|
|
|
SqlCount++;
|
|
|
|
|
}
|
|
|
|
|
base.OnExecutedCommand(cmd);
|
|
|
|
|
}
|
2015-01-09 15:27:47 +11:00
|
|
|
}
|
2012-12-12 03:47:04 +05:00
|
|
|
}
|