Created
April 4, 2018 13:51
-
-
Save Vivelin/29eddb80a43c22ca093c5d998f875bc0 to your computer and use it in GitHub Desktop.
An Entity Framework IDbInterceptor that suppresses execution of insert and update statements and instead writes them to a TextWriter.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Data.Common; | |
using System.Data.Entity.Infrastructure.Interception; | |
using System.Data.SqlClient; | |
using System.IO; | |
/// <summary> | |
/// Represents an interceptor that writes insert and update statements to a | |
/// <see cref="TextWriter"/> instead of executing them against a database. | |
/// </summary> | |
public class TextWriterDbInterceptor : IDbCommandInterceptor | |
{ | |
private DbConnection currentConnection; | |
/// <summary> | |
/// Initializes a new instance of the <see cref="TextWriterDbInterceptor"/> | |
/// class with the specified writer. | |
/// </summary> | |
/// <param name="writer"> | |
/// A <see cref="TextWriter"/> that receives intercepted statements. | |
/// </param> | |
public TextWriterDbInterceptor(TextWriter writer) | |
{ | |
Writer = writer; | |
} | |
/// <summary> | |
/// Gets a <see cref="TextWriter"/> that receives intercepted statements. | |
/// </summary> | |
public TextWriter Writer { get; } | |
/// <summary> | |
/// Gets or sets the database connection for the current command. | |
/// </summary> | |
protected DbConnection Connection | |
{ | |
get { return currentConnection; } | |
set | |
{ | |
if (value != currentConnection) | |
{ | |
currentConnection = value; | |
OnConnectionChanged(); | |
} | |
} | |
} | |
/// <summary> | |
/// This method is called after a call to <see | |
/// cref="M:System.Data.Common.DbCommand.ExecuteNonQuery"/> or one of its | |
/// async counterparts is made. The result used by Entity Framework can be | |
/// changed by setting <see cref="P:System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext`1.Result"/>. | |
/// </summary> | |
/// <remarks> | |
/// For async operations this method is not called until after the async task | |
/// has completed or failed. | |
/// </remarks> | |
/// <param name="command">The command being executed.</param> | |
/// <param name="interceptionContext"> | |
/// Contextual information associated with the call. | |
/// </param> | |
public void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext<int> interceptionContext) | |
{ | |
} | |
/// <summary> | |
/// This method is called before a call to <see | |
/// cref="M:System.Data.Common.DbCommand.ExecuteNonQuery"/> or one of its | |
/// async counterparts is made. | |
/// </summary> | |
/// <param name="command">The command being executed.</param> | |
/// <param name="interceptionContext"> | |
/// Contextual information associated with the call. | |
/// </param> | |
public void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext) | |
{ | |
if (!IsSelectStatement(command)) | |
{ | |
WriteCommand(command); | |
// In order to suppress execution, we also need to set a non-zero result; | |
// otherwise, Entity Framework will throw an exception which will prevent | |
// us from intercepting the other changes | |
interceptionContext.SuppressExecution(); | |
interceptionContext.Result = -1; | |
} | |
} | |
/// <summary> | |
/// This method is called after a call to <see | |
/// cref="M:System.Data.Common.DbCommand.ExecuteReader(System.Data.CommandBehavior)"/> | |
/// or one of its async counterparts is made. The result used by Entity | |
/// Framework can be changed by setting <see cref="P:System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext`1.Result"/>. | |
/// </summary> | |
/// <remarks> | |
/// For async operations this method is not called until after the async task | |
/// has completed or failed. | |
/// </remarks> | |
/// <param name="command">The command being executed.</param> | |
/// <param name="interceptionContext"> | |
/// Contextual information associated with the call. | |
/// </param> | |
public void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext) | |
{ | |
} | |
/// <summary> | |
/// This method is called before a call to <see | |
/// cref="M:System.Data.Common.DbCommand.ExecuteReader(System.Data.CommandBehavior)"/> | |
/// or one of its async counterparts is made. | |
/// </summary> | |
/// <param name="command">The command being executed.</param> | |
/// <param name="interceptionContext"> | |
/// Contextual information associated with the call. | |
/// </param> | |
public void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext) | |
{ | |
if (!IsSelectStatement(command)) | |
{ | |
// This is weird... | |
WriteCommand(command); | |
interceptionContext.SuppressExecution(); | |
} | |
} | |
/// <summary> | |
/// This method is called after a call to <see | |
/// cref="M:System.Data.Common.DbCommand.ExecuteScalar"/> or one of its async | |
/// counterparts is made. The result used by Entity Framework can be changed | |
/// by setting <see cref="P:System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext`1.Result"/>. | |
/// </summary> | |
/// <remarks> | |
/// For async operations this method is not called until after the async task | |
/// has completed or failed. | |
/// </remarks> | |
/// <param name="command">The command being executed.</param> | |
/// <param name="interceptionContext"> | |
/// Contextual information associated with the call. | |
/// </param> | |
public void ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext) | |
{ | |
} | |
/// <summary> | |
/// This method is called before a call to <see | |
/// cref="M:System.Data.Common.DbCommand.ExecuteScalar"/> or one of its async | |
/// counterparts is made. | |
/// </summary> | |
/// <param name="command">The command being executed.</param> | |
/// <param name="interceptionContext"> | |
/// Contextual information associated with the call. | |
/// </param> | |
public void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext) | |
{ | |
if (!IsSelectStatement(command)) | |
{ | |
WriteCommand(command); | |
interceptionContext.SuppressExecution(); | |
} | |
} | |
/// <summary> | |
/// Occurs when the <see cref="Connection"/> property has changed. | |
/// </summary> | |
protected virtual void OnConnectionChanged() | |
{ | |
Writer.WriteLine("-- {0}", Connection.DataSource); | |
Writer.WriteLine("USE [{0}]", Connection.Database); | |
Writer.WriteLine("GO"); | |
Writer.WriteLine(""); | |
} | |
/// <summary> | |
/// Writes an SQL statement to the current writer. | |
/// </summary> | |
/// <param name="command">A command that represents the SQL statement.</param> | |
protected virtual void WriteCommand(DbCommand command) | |
{ | |
Connection = command.Connection; | |
foreach (SqlParameter parameter in command.Parameters) | |
{ | |
WriteSqlParameter(parameter); | |
} | |
Writer.WriteLine(command.CommandText); | |
Writer.WriteLine("GO"); | |
Writer.WriteLine(); | |
} | |
/// <summary> | |
/// Writes an SQL parameter declaration to the current writer. | |
/// </summary> | |
/// <param name="parameter">An SQL parameter to write.</param> | |
protected virtual void WriteSqlParameter(SqlParameter parameter) | |
{ | |
Writer.WriteLine("DECLARE {0} {1} {2} = {3}", | |
FormatName(parameter), | |
parameter.SqlDbType.ToString(), | |
FormatDataSize(parameter), | |
FormatSqlValue(parameter)); | |
} | |
private static string FormatDataSize(SqlParameter parameter) | |
{ | |
if (parameter.Size == 0) | |
return ""; | |
if (parameter.Size == -1) | |
return "(MAX)"; | |
return $"({parameter.Size})"; | |
} | |
private static string FormatName(SqlParameter parameter) | |
{ | |
if (parameter.ParameterName.StartsWith("@")) | |
return parameter.ParameterName; | |
return $"@{parameter.ParameterName}"; | |
} | |
private static string FormatSqlValue(SqlParameter parameter) | |
{ | |
var value = Convert.ToString(parameter.SqlValue); | |
return $"'{value.Replace("'", "''")}'"; | |
} | |
private static bool IsSelectStatement(DbCommand command) | |
{ | |
return command.CommandText.StartsWith("select", StringComparison.OrdinalIgnoreCase); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment