Skip to content

Commit

Permalink
Wrapping Data layer errors
Browse files Browse the repository at this point in the history
Wrapping NetTiers errors thrown while accessing the database in a custom
Exception class that contains additional data.
  • Loading branch information
Jereme Guenther committed Sep 19, 2017
1 parent 86bc257 commit 4c00ba9
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 18 deletions.
104 changes: 86 additions & 18 deletions Source/DataAccessLayer/Utility.cst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
<%@ Property Name="RetrySleepTime" Optional="True" Type="System.Int32" Category="Retry" Description="The number of milliseconds that the current thread will sleep between attempts. Sleep only occurs if an exception is thrown." Default="1000" %>
<%@ Property Name="RetrySleepStyle" Optional="True" Type="MoM.Templates.SleepStyle" Category="Retry" Description="" %>

<%@ Property Name="CustomDataException" Optional="False" Type="System.Boolean" Category="Exceptions" Description="Enabling this option will cause the NetTiers data later to wrap its exceptions in a custom NetTiersDataException object with additional information." %>
<%@ Property Name="DbExecutionTimer" Optional="False" Type="System.Boolean" Category="Exceptions" Description="Enabling this option will add a Stopwatch timer count to the custom NetTiersDataException timing how long the database execution was running for." %>

using System;
using System.Text;
using System.Data;
Expand Down Expand Up @@ -442,6 +445,47 @@ namespace <%=DALNameSpace%>
#endregion
#endregion
}

/// <summary>
/// A custom Exception class for NetTiers to throw additional information
/// </summary>
public class NetTiersDataException : System.Exception
{
/// <summary>
/// Constructor to populate the base exception
/// </summary>
/// <param name="message">custom message</param>
/// <param name="ex">inner exception</param>
/// <param name="command">DB Command object</param>
public NetTiersDataException(string message, System.Exception ex, DbCommand command) : base(message, ex)
{
CommandObject = command;
}

/// <summary>
/// Constructor to populate the base exception with milliseconds included
/// </summary>
/// <param name="message">custom message</param>
/// <param name="ex">inner exception</param>
/// <param name="command">DB Command object</param>
/// <param name="milliseconds">How long the Data call was running for</param>
public NetTiersDataException(string message, System.Exception ex, DbCommand command, long milliseconds) : base(message, ex)
{
CommandObject = command;
ElapsedMilliseconds = milliseconds;
}

/// <summary>
/// The DbCommand object containing parameter values
/// </summary>
public DbCommand CommandObject = null;

/// <summary>
/// How long the Data call was running for
/// </summary>
public long ElapsedMilliseconds = 0;
}

}

<script runat="template">
Expand All @@ -455,38 +499,41 @@ private enum ExecuteType
}

private static string RetryExecuteCode =
@" {6}{3} results = {7};
@" {6}
{8}
{3} results = {7};
for (int attempt = 1; attempt <= {1}; attempt++)
{{
try
{{
results = {5}.Execute{0}(dbCommand{4});
break;
}}
catch (System.Data.SqlClient.SqlException)
catch (System.Data.SqlClient.SqlException {9})
{{
// we could time out due to locks
// or our database may be temporaryly unavailable
if (attempt == {1})
throw;

{{
{10}
}}
System.Threading.Thread.Sleep({2});
}}
}}

return results;";

private static string NoRetryExecuteCode =
@" {6}{3} results = {7};
@" {6}
{8}
{3} results = {7};
try
{{
results = {5}.Execute{0}(dbCommand{4});
}}
catch (Exception /*ex*/)
catch (Exception {9})
{{
//PSB 2006-05-26: should we use EntLib ExceptionPolicy.HandleException?
//if (ExceptionPolicy.HandleException(ex, exceptionPolicy)) throw;
throw;
{10}
}}
return results;";

Expand All @@ -496,6 +543,16 @@ private void WriteExecuteMethod(ExecuteType executeType, bool useTransactions)
string sleepType = string.Empty;
object[] formatParams = null;
bool useRetry = RetryEnabled && RetryMaxAttempts > 1;
string customExTimer = (CustomDataException && DbExecutionTimer) ?
@"System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch();
timer.Start();" : "";
string customEx = CustomDataException ? "ex" : "";
string customExThrow = (CustomDataException && DbExecutionTimer) ?
@"timer.Stop();
throw new NetTiersDataException(string.Format(""{0} Error: {{0}}"", dbCommand.CommandText), ex, dbCommand, timer.ElapsedMilliseconds);"
: CustomDataException ?
@"throw new NetTiersDataException(string.Format(""{0} Error: {{0}}"", dbCommand.CommandText), ex, dbCommand);"
: "throw;";

switch (RetrySleepStyle)
{
Expand All @@ -521,9 +578,11 @@ private void WriteExecuteMethod(ExecuteType executeType, bool useTransactions)
"IDataReader",
(useTransactions? ", transactionManager.TransactionObject":""),
(useTransactions? "transactionManager.Database":"database"),
(useTransactions? "if (!transactionManager.IsOpen) throw new DataException(\"Transaction must be open before executing a query.\");\r\n\t\t\t":""),
"null"

(useTransactions? "if (!transactionManager.IsOpen) throw new DataException(\"Transaction must be open before executing a query.\");":""),
"null",
customExTimer,
customEx,
string.Format(customExThrow, "Reader")
};
break;

Expand All @@ -536,8 +595,11 @@ private void WriteExecuteMethod(ExecuteType executeType, bool useTransactions)
"int",
(useTransactions? ", transactionManager.TransactionObject":""),
(useTransactions? "transactionManager.Database":"database"),
(useTransactions? "if (!transactionManager.IsOpen) throw new DataException(\"Transaction must be open before executing a query.\");\r\n\t\t\t":""),
"0"
(useTransactions? "if (!transactionManager.IsOpen) throw new DataException(\"Transaction must be open before executing a query.\");":""),
"0",
customExTimer,
customEx,
string.Format(customExThrow, "NonQuery")
};
break;

Expand All @@ -550,8 +612,11 @@ private void WriteExecuteMethod(ExecuteType executeType, bool useTransactions)
"DataSet",
(useTransactions? ", transactionManager.TransactionObject":""),
(useTransactions? "transactionManager.Database":"database"),
(useTransactions? "if (!transactionManager.IsOpen) throw new DataException(\"Transaction must be open before executing a query.\");\r\n\t\t\t":""),
"null"
(useTransactions? "if (!transactionManager.IsOpen) throw new DataException(\"Transaction must be open before executing a query.\");":""),
"null",
customExTimer,
customEx,
string.Format(customExThrow, "DataSet")
};
break;
case ExecuteType.Scalar:
Expand All @@ -563,8 +628,11 @@ private void WriteExecuteMethod(ExecuteType executeType, bool useTransactions)
"Object",
(useTransactions? ", transactionManager.TransactionObject":""),
(useTransactions? "transactionManager.Database":"database"),
(useTransactions? "if (!transactionManager.IsOpen) throw new DataException(\"Transaction must be open before executing a query.\");\r\n\t\t\t":""),
"null"
(useTransactions? "if (!transactionManager.IsOpen) throw new DataException(\"Transaction must be open before executing a query.\");":""),
"null",
customExTimer,
customEx,
string.Format(customExThrow, "Scalar")
};
break;
}
Expand Down
4 changes: 4 additions & 0 deletions Source/NetTiers.cst
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@
<%@ Property Name="LibraryPath" Type="System.String" Default="References" Category="07. CRUD - Advanced" Description="Path of the assembly library directory from the project output directory root." %>
<%@ Property Name="IncludeCustoms" Type="System.Boolean" Default="True" Category="07. CRUD - Advanced" Description="If true custom stored procedures (that starts with '_TableName_') will be detected and generated." %>
<%@ Property Name="CustomNonMatchingReturnType" Type="CustomNonMatchingReturnType" Default="DataSet" Category="07. CRUD - Advanced" Description="When using custom stored procedures, if the returned rows do not match the fields in an entity, a DataSet or IDataReader will be returned. Choose One. This is useful if you've returned more than one resultset in a custom procedure; you can use a ConvertToDataSet(IDataReader) method in the Utility class to convert that to a DataSet." %>
<%@ Property Name="CustomDataException" Optional="False" Type="System.Boolean" Category="07. CRUD - Advanced" Default="true" Description="Enabling this option will cause the NetTiers data later to wrap its exceptions in a custom NetTiersDataException object with additional information." %>
<%@ Property Name="DbExecutionTimer" Optional="False" Type="System.Boolean" Category="07. CRUD - Advanced" Default="true" Description="Enabling this option will add a Stopwatch timer count to the custom NetTiersDataException timing how long the database execution was running for." %>

<%@ Property Name="IncludeDrop" Type="System.Boolean" Default="True" Category="07. CRUD - Advanced" Description="If True then drop statements will be generated in accordance with the DropStyle. If False then no procedures will be droppped" %>
<%@ Property Name="DropStyle" Type="DropStyleEnum" Default="Entity" Category="07. CRUD - Advanced" Description="If Entity then drop statements will be generated to drop existing stored procedures for procedures being generated. If All then all existing netTiers procedures that match the ProcedurePrefix (one must be specified) and do not match the CustomProceduresStartsWith will be droppped." %>
Expand Down Expand Up @@ -2015,6 +2017,8 @@
this.GetTemplate("Utility.cst").SetProperty("RetrySleepTime", RetrySleepTime);
this.GetTemplate("Utility.cst").SetProperty("RetrySleepStyle", RetrySleepStyle);
this.GetTemplate("Utility.cst").SetProperty("ProviderInvariantName", ProviderInvariantName);
this.GetTemplate("Utility.cst").SetProperty("CustomDataException", CustomDataException);
this.GetTemplate("Utility.cst").SetProperty("DbExecutionTimer", DbExecutionTimer);
this.RenderToFile("Utility.cst", rootPathDAL + "\\Utility.cs", true);

AddFileNode(commonNode, "ITransactionManager.cs");
Expand Down

0 comments on commit 4c00ba9

Please sign in to comment.