using System.Collections.Generic;
namespace abc.ArgumentValidation
public static class ArgumentValidator
internal sealed class ValidatedNotNullAttribute : Attribute
public static T CheckForNullReference<T>([ValidatedNotNull][NoEnumeration] T parameterValue, string parameterName) where T : class
CheckParameterName(parameterName);
if (parameterValue == null)
throw new ArgumentNullException(parameterName);
public static T CheckForNullReference<T>([ValidatedNotNull][NoEnumeration] T parameterValue, string parameterName, string message) where T : class
CheckParameterName(parameterName);
if (parameterValue == null)
throw new ArgumentNullException(parameterName, message);
public static T CheckForNullReference<T>(T? parameterValue, string parameterName) where T : struct
CheckParameterName(parameterName);
if (!parameterValue.HasValue)
throw new ArgumentNullException(parameterName);
return parameterValue.Value;
public static string CheckForNullOrEmptyString([ValidatedNotNull] string parameterValue, string parameterName)
return CheckForNullOrEmptyString(parameterValue, parameterName, "Value cannot be null or empty.");
public static string CheckForNullOrEmptyString([ValidatedNotNull] string parameterValue, string parameterName, string message)
CheckParameterName(parameterName);
if (string.IsNullOrEmpty(parameterValue))
throw new ArgumentException(message, parameterName);
public static void ConditionalArgumentException(bool throwIf, string parameterName, string message)
CheckParameterName(parameterName);
throw new ArgumentException(message, parameterName);
public static T[] CheckArrayNotNullOrEmpty<T>([ValidatedNotNull] T[] parameterValue, string parameterName)
CheckForNullReference(parameterValue, parameterName);
if (parameterValue.Length == 0)
throw new ArgumentException("Array cannot be empty.", parameterName);
public static T[] CheckArrayAboveMinimumLength<T>([ValidatedNotNull] T[] parameterValue, string parameterName, int minSize)
CheckForNullReference(parameterValue, parameterName);
if (parameterValue.Length < minSize)
throw new ArgumentException(string.Format("Array was expected to contain at least [{0}] element(s) but actually contained [{1}]", minSize, parameterValue.Length), parameterName);
public static T[] CheckArrayLength<T>([ValidatedNotNull] T[] parameterValue, string parameterName, int length)
CheckForNullReference(parameterValue, parameterName);
if (parameterValue.Length != length)
throw new ArgumentException(string.Format("Array was expected to contain [{0}] element(s) but actually contained [{1}]", length, parameterValue.Length), parameterName);
public static T CheckTypeCorrectAndCast<T>(object parameterValue, string parameterName) where T : class
CheckForNullReference(parameterValue, parameterName);
if (typeof(T) != parameterValue.GetType())
throw new ArgumentException(string.Format("Value is not of correct type, expected: {0} actual: {1}", typeof(T).Name, parameterValue.GetType().Name), parameterName);
return (T)parameterValue;
public static T CheckTypeAssignableToAndCast<T>(object parameterValue, string parameterName) where T : class
CheckForNullReference(parameterValue, parameterName);
if (!(parameterValue is T))
throw new ArgumentException(string.Format("Value is not of correct type, expected: {0} actual: {1}", typeof(T).Name, parameterValue.GetType().Name), parameterName);
return (T) parameterValue;
public static T CheckAndParseEnumeration<T>(string parameterValue, string parameterName, bool ignoreCase)
CheckForNullReference(parameterValue, parameterName);
Type enumType = typeof(T);
return (T)Enum.Parse(enumType, parameterValue, ignoreCase);
catch (ArgumentException exception)
string exceptionMessage = string.Format("Enumeration [{0}] is not defined for [{1}] parameter [{2}], value [{3}]", enumType.FullName,
ignoreCase ? "case-insensitive" : "case-sensitive", parameterName, parameterValue);
throw new ArgumentException(exceptionMessage, exception);
public static void CheckParameterBelongsToList(string parameterValue, string parameterName, List<string> expectedList )
CheckForNullReference(parameterValue, parameterName);
CheckForNullReference(expectedList, "ExpectedParameterList");
if (!expectedList.Contains(parameterValue, StringComparer.OrdinalIgnoreCase))
throw new ArgumentException(string.Format("Parameter value '{0}' does not belong to the expected list of parameters for Parameter name '{1}'", parameterValue, parameterName));
private static void CheckParameterName(string parameterName)
if (string.IsNullOrEmpty(parameterName))
throw new ArgumentException("Value cannot be null or empty.", "parameterName");
CREATE PROCEDURE [dbo].[generate_audit_trigger] @TableName VARCHAR(250)
DECLARE @TriggerName AS VARCHAR(106)
SET @TriggerName = 'tr_' + @TableName + '_audit'
DECLARE @Date AS VARCHAR(11)
SET @Date = CONVERT(VARCHAR(11),GETUTCDATE(),106)
DECLARE @HasCheckSum AS BIT = 0
DECLARE @Schema VARCHAR(100)
DECLARE @NextCol VARCHAR(100)
SELECT @Schema = s.name FROM sys.schemas s JOIN sys.objects o
ON o.schema_id = s.schema_id and o.name = @TableName
PRINT 'Cannot Find Table ' + @TableName
PRINT '--** Object: Trigger '+ @TriggerName + ' Generated by generate_audit_trigger for user ' + USER_NAME() + ' Script Date: ' + @Date
PRINT 'IF EXISTS (SELECT 1 FROM sys.triggers WHERE name = ''' + @TriggerName + ''')
DROP TRIGGER ' + @Schema + '.' + @TriggerName
PRINT '/* *********************************************************************************
** Name: ' + @TriggerName
PRINT '** Description: Update/Delete trigger creation script for ' + @TableName
**********************************************************************************
** Change No: Date: Author: Description:
** _________ ___________ ______ ______________________________________
** 001 '+@Date+' generate_audit_trigger Generated.
** ****************************************************************************** */'
PRINT 'CREATE TRIGGER ' + @TriggerName + ' ON '+@Schema+'.' + @TableName
PRINT 'AFTER UPDATE,DELETE AS
SET NOCOUNT ON --to avoid the rowcount messages
SET ROWCOUNT 0 --in case the client has modified the rowcount
DECLARE @UserName VARCHAR(100)
DECLARE @StartTime DATETIME
DECLARE @audit_action CHAR(1)
IF EXISTS (SELECT * FROM Inserted)
IF EXISTS (SELECT * FROM Deleted)
SELECT @audit_action =''U''
SELECT @audit_action =''I''
SELECT @audit_action =''D''
SET @UserName = SYSTEM_USER
SET @StartTime = GETDATE()
INSERT INTO ' + 'audit' + '.audit_' + @TableName
-- Now we need to build up our columnlist for the table
DECLARE q1 CURSOR FOR SELECT name FROM sys.columns WHERE object_name(object_id) = @TableName ORDER BY column_id
IF @NextCol = 'CheckSumValue' SET @HasCheckSum = 1
IF @@FETCH_STATUS = 0 PRINT ' ,' + @NextCol
PRINT ' SELECT d.' + @NextCol
IF @@FETCH_STATUS = 0 PRINT ' ,d.' + @NextCol
PRINT ' ,audit_action = @audit_action
DECLARE q1 CURSOR FOR SELECT c.name FROM sys.columns c
JOIN sys.index_columns ic ON c.column_id = ic.column_id AND c.object_id = ic.object_id
JOIN sys.indexes i ON ic.index_id = i.index_id AND ic.object_id = i.object_id
WHERE i.is_primary_key = 1 AND Object_name(i.object_id) = @TableName
PRINT ' LEFT JOIN Inserted i ON i.' + @NextCol + ' = d.' + @NextCol
IF @@FETCH_STATUS = 0 PRINT ' AND i.' + @NextCol + ' = d.'+@NextCol
IF @HasCheckSum = 1 PRINT ' WHERE ISNULL(i.checkSumValue,-1) <> d.CheckSumValue'
DECLARE @ErrorMessage NVARCHAR(4000)
DECLARE @ErrorSeverity INT
SELECT @ErrorMessage = ERROR_MESSAGE()
,@ErrorNumber = ERROR_NUMBER()
,@ErrorSeverity = ERROR_SEVERITY()
,@ErrorState = ERROR_STATE()
,@ErrorLine = ERROR_LINE()
RAISERROR (@ErrorMessage,
CREATE PROCEDURE [dbo].[generate_audit_table] @TableName VARCHAR(250)
DECLARE @String varchar(8000), @String2 varchar(8000), @Schema VARCHAR(100)
-- Assign table name to output
SELECT @Schema = s.name FROM sys.schemas s JOIN sys.objects o
ON o.schema_id = s.schema_id and o.name = @TableName
PRINT 'Cannot Find Table ' + @TableName
PRINT 'IF EXISTS (SELECT 1 FROM sys.tables WHERE type = ''U'' AND name = ''audit_' + @TableName + ''' AND schema_name(schema_id) = ''' + 'audit' +''')'
PRINT ' DROP TABLE ' + 'audit' + '.audit_' + @TableName
DECLARE @Date AS VARCHAR(11)
SET @Date = CONVERT(VARCHAR(11),GETDATE(),106)
PRINT '/* *********************************************************************************
** Name: ' + 'audit' + '.audit_' + @TableName
PRINT '** Description: Audit Table Creation Script script for ' + @Schema + '.zAudit' + @TableName
**********************************************************************************
** Change No: Date: Author: Description:
** _________ ___________ ______ ______________________________________
** 001 '+@Date+' generate_audit_table Generated.
** ****************************************************************************** */'
SET @String = 'CREATE TABLE ' + 'audit' + '.audit_' + @TableName + '(audit_id int identity,'
SELECT @String2 = @String2 +
sys.columns.name+' '+sys.types.name
-- Need Precision and Scale for decimal and numeric
WHEN sys.columns.system_type_id IN (106, 108) THEN '('+CONVERT(varchar,sys.columns.precision)+', '+CONVERT(varchar,sys.columns.scale)+')'
-- Need Size for varchar, char, nvarchar, and nchar
WHEN sys.columns.system_type_id IN (167, 175, 231, 239,165) THEN CASE WHEN sys.columns.max_length = -1 THEN '(max)' ELSE '('+CONVERT(varchar,sys.columns.max_length)+')' END
-- Do not need more for image, text, uniqueidentifier, tinyint, smallint, int, smalldatetime, real, money, datetime, float, sql_variant, ntext, bit, smallmoney, bigint, varbinary, binary, timestamp
-- (34, 35, 36, 48, 52, 56, 58, 59, 60, 61, 62, 98, 99, 104, 122, 127, 165, 173, 189)
INNER JOIN sys.objects ON sys.columns.object_id = sys.objects.object_id
INNER JOIN sys.types ON sys.columns.system_type_id = sys.types.system_type_id
WHERE sys.objects.name = @TableName
ORDER BY sys.columns.column_id
SET @String2 = @String2 + 'audit_user VARCHAR(100) NOT NULL DEFAULT SYSTEM_USER,' + CHAR(10)
SET @String2 = @String2 + 'audit_date_time DATETIME2 NOT NULL DEFAULT GETDATE(),' + CHAR(10)
SET @String2 = @String2 + 'audit_action CHAR(1) NOT NULL)'