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 15:50:38 +02:00
using System.Text ;
2013-05-10 10:15:30 -02:00
using StackExchange.Profiling ;
2013-01-18 09:00:18 -01:00
using Umbraco.Core.Logging ;
2016-08-09 15:33:35 +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>
/// Represents the Umbraco implementation of the PetaPoco Database object
/// </summary>
/// <remarks>
2016-02-04 14:51:19 +01:00
/// Currently this object exists for 'future proofing' our implementation. By having our own inheritied implementation we
2015-01-09 15:27:47 +11:00
/// can then override any additional execution (such as additional loggging, functionality, etc...) that we need to without breaking compatibility since we'll always be exposing
2016-02-04 14:51:19 +01:00
/// this object instead of the base PetaPoco database object.
2015-01-09 15:27:47 +11:00
/// </remarks>
2013-09-30 12:02:35 +10:00
public class UmbracoDatabase : Database , IDisposeOnRequestEnd
2015-01-09 15:27:47 +11:00
{
private readonly ILogger _logger ;
2013-01-18 09:00:18 -01:00
private readonly Guid _instanceId = Guid . NewGuid ( ) ;
2015-07-22 12:10:21 +02:00
private bool _enableCount ;
2015-01-09 15:27:47 +11:00
/// <summary>
/// Used for testing
/// </summary>
internal Guid InstanceId
{
get { return _instanceId ; }
}
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 ; }
2015-01-09 15:27:47 +11:00
[Obsolete("Use the other constructor specifying an ILogger instead")]
public UmbracoDatabase ( IDbConnection connection )
: this ( connection , LoggerResolver . Current . Logger )
{
}
[Obsolete("Use the other constructor specifying an ILogger instead")]
public UmbracoDatabase ( string connectionString , string providerName )
: this ( connectionString , providerName , LoggerResolver . Current . Logger )
{
}
[Obsolete("Use the other constructor specifying an ILogger instead")]
public UmbracoDatabase ( string connectionString , DbProviderFactory provider )
: this ( connectionString , provider , LoggerResolver . Current . Logger )
{
}
[Obsolete("Use the other constructor specifying an ILogger instead")]
public UmbracoDatabase ( string connectionStringName )
: this ( connectionStringName , LoggerResolver . Current . Logger )
{
}
public UmbracoDatabase ( IDbConnection connection , ILogger logger )
: base ( connection )
{
_logger = logger ;
2015-07-22 12:10:21 +02:00
EnableSqlTrace = false ;
2015-01-09 15:27:47 +11:00
}
public UmbracoDatabase ( string connectionString , string providerName , ILogger logger )
: base ( connectionString , providerName )
{
_logger = logger ;
2015-07-22 12:10:21 +02:00
EnableSqlTrace = false ;
2015-01-09 15:27:47 +11:00
}
public UmbracoDatabase ( string connectionString , DbProviderFactory provider , ILogger logger )
: base ( connectionString , provider )
{
_logger = logger ;
2015-07-22 12:10:21 +02:00
EnableSqlTrace = false ;
2015-01-09 15:27:47 +11:00
}
public UmbracoDatabase ( string connectionStringName , ILogger logger )
: base ( connectionStringName )
{
_logger = logger ;
2015-07-22 12:10:21 +02:00
EnableSqlTrace = false ;
2015-01-09 15:27:47 +11:00
}
2013-01-18 09:00:18 -01:00
2013-05-10 10:15:30 -02:00
public override IDbConnection OnConnectionOpened ( IDbConnection connection )
{
2016-02-04 14:51:19 +01:00
// propagate timeout if none yet
// wrap the connection with a profiling connection that tracks timings
2013-05-10 10:15:30 -02:00
return new StackExchange . Profiling . Data . ProfiledDbConnection ( connection as DbConnection , MiniProfiler . Current ) ;
}
2015-01-09 15:27:47 +11:00
2013-01-18 09:00:18 -01:00
public override void OnException ( Exception x )
{
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-02-04 14:51:19 +01:00
public override void OnExecutingCommand ( IDbCommand cmd )
{
// 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 15:50:38 +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 ) ;
}
2015-07-22 12:10:21 +02:00
public override void OnExecutedCommand ( IDbCommand cmd )
{
if ( _enableCount )
{
SqlCount + + ;
}
base . OnExecutedCommand ( cmd ) ;
}
2016-08-09 15:33:35 +02:00
/// <summary>
/// We are overriding this in the case that we are using SQL Server 2012+ so we can make paging more efficient than the default PetaPoco paging
/// see: http://issues.umbraco.org/issue/U4-8837
/// </summary>
/// <param name="sql"></param>
/// <param name="sqlSelectRemoved"></param>
/// <param name="sqlOrderBy"></param>
/// <param name="args"></param>
/// <param name="sqlPage"></param>
/// <param name="databaseType"></param>
/// <param name="skip"></param>
/// <param name="take"></param>
internal override void BuildSqlDbSpecificPagingQuery ( DBType databaseType , long skip , long take , string sql , string sqlSelectRemoved , string sqlOrderBy , ref object [ ] args , out string sqlPage )
{
if ( databaseType = = DBType . SqlServer )
{
//we need to check it's version to see what kind of paging format we can use
//TODO: This is a hack, but we don't have access to the SqlSyntaxProvider here, we can in v8 but not now otherwise
// this would be a breaking change.
var sqlServerSyntax = SqlSyntaxContext . SqlSyntaxProvider as SqlServerSyntaxProvider ;
if ( sqlServerSyntax ! = null )
{
if ( ( int ) sqlServerSyntax . GetVersionName ( this ) > = ( int ) SqlServerVersionName . V2012 )
{
//we can use the good paging! to do that we are going to change the databaseType to SQLCE since
//it also uses the good paging syntax.
base . BuildSqlDbSpecificPagingQuery ( DBType . SqlServerCE , skip , take , sql , sqlSelectRemoved , sqlOrderBy , ref args , out sqlPage ) ;
return ;
}
}
}
//use the defaults
base . BuildSqlDbSpecificPagingQuery ( databaseType , skip , take , sql , sqlSelectRemoved , sqlOrderBy , ref args , out sqlPage ) ;
}
2015-01-09 15:27:47 +11:00
}
2012-12-12 03:47:04 +05:00
}