diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..a1486bd
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,42 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ // Use IntelliSense to find out which attributes exist for C# debugging
+ // Use hover for the description of the existing attributes
+ // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
+ "name": ".NET Core Launch (console)",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build",
+ // If you have changed target frameworks, make sure to update the program path.
+ "program": "${workspaceFolder}/Dapper.SimpleCRUDTests/bin/Debug/netcoreapp3.1/Dapper.SimpleCRUDTests.dll",
+ "args": [],
+ "cwd": "${workspaceFolder}/Dapper.SimpleCRUDTests",
+ // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
+ "console": "internalConsole",
+ "stopAtEntry": false
+ },
+ {
+ // Use IntelliSense to find out which attributes exist for C# debugging
+ // Use hover for the description of the existing attributes
+ // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
+ "name": ".NET Core Launch (console) - FluentTableMap",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build",
+ // If you have changed target frameworks, make sure to update the program path.
+ "program": "${workspaceFolder}/Dapper.SimpleCRUDFluentTableMap.Tests/bin/Debug/netcoreapp3.1/Dapper.SimpleCRUDFluentTableMap.Tests.dll",
+ "args": [],
+ "cwd": "${workspaceFolder}/Dapper.SimpleCRUDFluentTableMap.Tests",
+ // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
+ "console": "internalConsole",
+ "stopAtEntry": false
+ },
+ {
+ "name": ".NET Core Attach",
+ "type": "coreclr",
+ "request": "attach"
+ }
+ ]
+}
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 0000000..fd866d6
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,42 @@
+{
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "build",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "build",
+ "${workspaceFolder}",
+ "/property:GenerateFullPaths=true",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "label": "publish",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "publish",
+ "${workspaceFolder}/Dapper.SimpleCRUDTests/Dapper.SimpleCRUDTests.csproj",
+ "/property:GenerateFullPaths=true",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "label": "watch",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "watch",
+ "run",
+ "${workspaceFolder}/Dapper.SimpleCRUDTests/Dapper.SimpleCRUDTests.csproj",
+ "/property:GenerateFullPaths=true",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Dapper.SimpleCRUD.sln b/Dapper.SimpleCRUD.sln
index b9a323e..850965b 100644
--- a/Dapper.SimpleCRUD.sln
+++ b/Dapper.SimpleCRUD.sln
@@ -14,6 +14,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapper.SimpleCRUD", "Dapper
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapper.SimpleCRUDTests", "Dapper.SimpleCRUDTests\Dapper.SimpleCRUDTests.csproj", "{165025ED-34C7-4519-BEDC-37B2859735AF}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapper.SimpleCRUDFluentTableMap", "Dapper.SimpleCRUDFluentTableMap\Dapper.SimpleCRUDFluentTableMap.csproj", "{F55831AC-F972-43C3-A742-EBB5D0908C8E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapper.SimpleCRUDFluentTableMap.Tests", "Dapper.SimpleCRUDFluentTableMap.Tests\Dapper.SimpleCRUDFluentTableMap.Tests.csproj", "{E699CE93-AC81-4F3F-9BA5-2BC7C51DB87C}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -48,6 +52,30 @@ Global
{165025ED-34C7-4519-BEDC-37B2859735AF}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{165025ED-34C7-4519-BEDC-37B2859735AF}.Release|x86.ActiveCfg = Release|Any CPU
{165025ED-34C7-4519-BEDC-37B2859735AF}.Release|x86.Build.0 = Release|Any CPU
+ {F55831AC-F972-43C3-A742-EBB5D0908C8E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F55831AC-F972-43C3-A742-EBB5D0908C8E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F55831AC-F972-43C3-A742-EBB5D0908C8E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {F55831AC-F972-43C3-A742-EBB5D0908C8E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {F55831AC-F972-43C3-A742-EBB5D0908C8E}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {F55831AC-F972-43C3-A742-EBB5D0908C8E}.Debug|x86.Build.0 = Debug|Any CPU
+ {F55831AC-F972-43C3-A742-EBB5D0908C8E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F55831AC-F972-43C3-A742-EBB5D0908C8E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F55831AC-F972-43C3-A742-EBB5D0908C8E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {F55831AC-F972-43C3-A742-EBB5D0908C8E}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {F55831AC-F972-43C3-A742-EBB5D0908C8E}.Release|x86.ActiveCfg = Release|Any CPU
+ {F55831AC-F972-43C3-A742-EBB5D0908C8E}.Release|x86.Build.0 = Release|Any CPU
+ {E699CE93-AC81-4F3F-9BA5-2BC7C51DB87C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E699CE93-AC81-4F3F-9BA5-2BC7C51DB87C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E699CE93-AC81-4F3F-9BA5-2BC7C51DB87C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {E699CE93-AC81-4F3F-9BA5-2BC7C51DB87C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {E699CE93-AC81-4F3F-9BA5-2BC7C51DB87C}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {E699CE93-AC81-4F3F-9BA5-2BC7C51DB87C}.Debug|x86.Build.0 = Debug|Any CPU
+ {E699CE93-AC81-4F3F-9BA5-2BC7C51DB87C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E699CE93-AC81-4F3F-9BA5-2BC7C51DB87C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E699CE93-AC81-4F3F-9BA5-2BC7C51DB87C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {E699CE93-AC81-4F3F-9BA5-2BC7C51DB87C}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {E699CE93-AC81-4F3F-9BA5-2BC7C51DB87C}.Release|x86.ActiveCfg = Release|Any CPU
+ {E699CE93-AC81-4F3F-9BA5-2BC7C51DB87C}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/Dapper.SimpleCRUD/SimpleCRUD.cs b/Dapper.SimpleCRUD/SimpleCRUD.cs
index 691075c..69fe753 100644
--- a/Dapper.SimpleCRUD/SimpleCRUD.cs
+++ b/Dapper.SimpleCRUD/SimpleCRUD.cs
@@ -7,7 +7,7 @@
using System.Reflection;
using System.Text;
using Microsoft.CSharp.RuntimeBinder;
-
+
namespace Dapper
{
///
@@ -15,7 +15,7 @@ namespace Dapper
///
public static partial class SimpleCRUD
{
-
+
static SimpleCRUD()
{
SetDialect(_dialect);
@@ -25,16 +25,15 @@ static SimpleCRUD()
private static string _encapsulation;
private static string _getIdentitySql;
private static string _getPagedListSql;
-
- private static readonly ConcurrentDictionary TableNames = new ConcurrentDictionary();
- private static readonly ConcurrentDictionary ColumnNames = new ConcurrentDictionary();
-
+
private static readonly ConcurrentDictionary StringBuilderCacheDict = new ConcurrentDictionary();
private static bool StringBuilderCacheEnabled = true;
-
- private static ITableNameResolver _tableNameResolver = new TableNameResolver();
- private static IColumnNameResolver _columnNameResolver = new ColumnNameResolver();
-
+
+ private static TableMapResolver _tableMapResolver = new TableMapResolver();
+ private static IColumnPropertiesResolver _columnPropertiesResolver = _tableMapResolver;
+ private static ITableNameResolver _tableNameResolver = _tableMapResolver;
+ private static IColumnNameResolver _columnNameResolver = _tableMapResolver;
+
///
/// Append a Cached version of a strinbBuilderAction result based on a cacheKey
///
@@ -48,14 +47,14 @@ private static void StringBuilderCache(StringBuilder sb, string cacheKey, Action
sb.Append(value);
return;
}
-
+
StringBuilder newSb = new StringBuilder();
stringBuilderAction(newSb);
value = newSb.ToString();
StringBuilderCacheDict.AddOrUpdate(cacheKey, value, (t, v) => value);
sb.Append(value);
}
-
+
///
/// Returns the current dialect name
///
@@ -64,9 +63,9 @@ public static string GetDialect()
{
return _dialect.ToString();
}
-
+
///
- /// Sets the database dialect
+ /// Sets the database dialect
///
///
public static void SetDialect(Dialect dialect)
@@ -111,7 +110,17 @@ public static void SetDialect(Dialect dialect)
break;
}
}
-
+
+ ///
+ /// Sets the Column Map resolver
+ /// used to resolve column properties attributes
+ ///
+ /// The resolver to use when requesting the format of a table name
+ public static void SetColumnPropertiesResolver(IColumnPropertiesResolver resolver)
+ {
+ _columnPropertiesResolver = resolver;
+ }
+
///
/// Sets the table name resolver
///
@@ -120,16 +129,17 @@ public static void SetTableNameResolver(ITableNameResolver resolver)
{
_tableNameResolver = resolver;
}
-
+
///
/// Sets the column name resolver
///
/// The resolver to use when requesting the format of a column name
+ [Obsolete("use SetColumnPropertiesResolver instead")]
public static void SetColumnNameResolver(IColumnNameResolver resolver)
{
_columnNameResolver = resolver;
}
-
+
///
/// By default queries the table matching the class name
/// -Table name can be overridden by adding an attribute on your class [Table("YourTableName")]
@@ -148,24 +158,24 @@ public static T Get(this IDbConnection connection, object id, IDbTransaction
{
var currenttype = typeof(T);
var idProps = GetIdProperties(currenttype).ToList();
-
+
if (!idProps.Any())
throw new ArgumentException("Get only supports an entity with a [Key] or Id property");
-
+
var name = GetTableName(currenttype);
var sb = new StringBuilder();
sb.Append("Select ");
//create a new empty instance of the type to get the base properties
BuildSelect(sb, GetScaffoldableProperties().ToArray());
sb.AppendFormat(" from {0} where ", name);
-
+
for (var i = 0; i < idProps.Count; i++)
{
if (i > 0)
sb.Append(" and ");
sb.AppendFormat("{0} = @{1}", GetColumnName(idProps[i]), idProps[i].Name);
}
-
+
var dynParms = new DynamicParameters();
if (idProps.Count == 1)
dynParms.Add("@" + idProps.First().Name, id);
@@ -174,13 +184,13 @@ public static T Get(this IDbConnection connection, object id, IDbTransaction
foreach (var prop in idProps)
dynParms.Add("@" + prop.Name, id.GetType().GetProperty(prop.Name).GetValue(id, null));
}
-
+
if (Debugger.IsAttached)
Trace.WriteLine(String.Format("Get<{0}>: {1} with Id: {2}", currenttype, sb, id));
-
+
return connection.Query(sb.ToString(), dynParms, transaction, true, commandTimeout).FirstOrDefault();
}
-
+
///
/// By default queries the table matching the class name
/// -Table name can be overridden by adding an attribute on your class [Table("YourTableName")]
@@ -198,26 +208,26 @@ public static IEnumerable GetList(this IDbConnection connection, object wh
{
var currenttype = typeof(T);
var name = GetTableName(currenttype);
-
+
var sb = new StringBuilder();
var whereprops = GetAllProperties(whereConditions).ToArray();
sb.Append("Select ");
//create a new empty instance of the type to get the base properties
BuildSelect(sb, GetScaffoldableProperties().ToArray());
sb.AppendFormat(" from {0}", name);
-
+
if (whereprops.Any())
{
sb.Append(" where ");
BuildWhere(sb, whereprops, whereConditions);
}
-
+
if (Debugger.IsAttached)
Trace.WriteLine(String.Format("GetList<{0}>: {1}", currenttype, sb));
-
+
return connection.Query(sb.ToString(), whereConditions, transaction, true, commandTimeout);
}
-
+
///
/// By default queries the table matching the class name
/// -Table name can be overridden by adding an attribute on your class [Table("YourTableName")]
@@ -237,21 +247,21 @@ public static IEnumerable GetList(this IDbConnection connection, string co
{
var currenttype = typeof(T);
var name = GetTableName(currenttype);
-
+
var sb = new StringBuilder();
sb.Append("Select ");
//create a new empty instance of the type to get the base properties
BuildSelect(sb, GetScaffoldableProperties().ToArray());
sb.AppendFormat(" from {0}", name);
-
+
sb.Append(" " + conditions);
-
+
if (Debugger.IsAttached)
Trace.WriteLine(String.Format("GetList<{0}>: {1}", currenttype, sb));
-
+
return connection.Query(sb.ToString(), parameters, transaction, true, commandTimeout);
}
-
+
///
/// By default queries the table matching the class name
/// -Table name can be overridden by adding an attribute on your class [Table("YourTableName")]
@@ -264,7 +274,7 @@ public static IEnumerable GetList(this IDbConnection connection)
{
return connection.GetList(new { });
}
-
+
///
/// By default queries the table matching the class name
/// -Table name can be overridden by adding an attribute on your class [Table("YourTableName")]
@@ -288,15 +298,15 @@ public static IEnumerable GetListPaged(this IDbConnection connection, int
{
if (string.IsNullOrEmpty(_getPagedListSql))
throw new Exception("GetListPage is not supported with the current SQL Dialect");
-
+
if (pageNumber < 1)
throw new Exception("Page must be greater than 0");
-
+
var currenttype = typeof(T);
var idProps = GetIdProperties(currenttype).ToList();
if (!idProps.Any())
throw new ArgumentException("Entity must have at least one [Key] property");
-
+
var name = GetTableName(currenttype);
var sb = new StringBuilder();
var query = _getPagedListSql;
@@ -304,7 +314,7 @@ public static IEnumerable GetListPaged(this IDbConnection connection, int
{
orderby = GetColumnName(idProps.First());
}
-
+
//create a new empty instance of the type to get the base properties
BuildSelect(sb, GetScaffoldableProperties().ToArray());
query = query.Replace("{SelectColumns}", sb.ToString());
@@ -314,13 +324,13 @@ public static IEnumerable GetListPaged(this IDbConnection connection, int
query = query.Replace("{OrderBy}", orderby);
query = query.Replace("{WhereClause}", conditions);
query = query.Replace("{Offset}", ((pageNumber - 1) * rowsPerPage).ToString());
-
+
if (Debugger.IsAttached)
Trace.WriteLine(String.Format("GetListPaged<{0}>: {1}", currenttype, query));
-
+
return connection.Query(query, parameters, transaction, true, commandTimeout);
}
-
+
///
/// Inserts a row into the database
/// By default inserts into the table matching the class name
@@ -339,7 +349,7 @@ public static IEnumerable GetListPaged(this IDbConnection connection, int
{
return Insert(connection, entityToInsert, transaction, commandTimeout);
}
-
+
///
/// Inserts a row into the database, using ONLY the properties defined by TEntity
/// By default inserts into the table matching the class name
@@ -364,10 +374,10 @@ public static TKey Insert(this IDbConnection connection, TEntity
.Invoke(null, new object[] { connection,entityToInsert,transaction,commandTimeout });
}
var idProps = GetIdProperties(entityToInsert).ToList();
-
+
if (!idProps.Any())
throw new ArgumentException("Insert only supports an entity with a [Key] or Id property");
-
+
var keyHasPredefinedValue = false;
var baseType = typeof(TKey);
var underlyingType = Nullable.GetUnderlyingType(baseType);
@@ -376,7 +386,7 @@ public static TKey Insert(this IDbConnection connection, TEntity
{
throw new Exception("Invalid return type");
}
-
+
var name = GetTableName(entityToInsert);
var sb = new StringBuilder();
sb.AppendFormat("insert into {0}", name);
@@ -387,7 +397,7 @@ public static TKey Insert(this IDbConnection connection, TEntity
sb.Append(" (");
BuildInsertValues(sb);
sb.Append(")");
-
+
if (keytype == typeof(Guid))
{
var guidvalue = (Guid)idProps.First().GetValue(entityToInsert, null);
@@ -402,7 +412,7 @@ public static TKey Insert(this IDbConnection connection, TEntity
}
sb.Append(";select '" + idProps.First().GetValue(entityToInsert, null) + "' as id");
}
-
+
if ((keytype == typeof(int) || keytype == typeof(long)) && Convert.ToInt64(idProps.First().GetValue(entityToInsert, null)) == 0)
{
sb.Append(";" + _getIdentitySql);
@@ -411,19 +421,19 @@ public static TKey Insert(this IDbConnection connection, TEntity
{
keyHasPredefinedValue = true;
}
-
+
if (Debugger.IsAttached)
Trace.WriteLine(String.Format("Insert: {0}", sb));
-
+
var r = connection.Query(sb.ToString(), entityToInsert, transaction, true, commandTimeout);
-
+
if (keytype == typeof(Guid) || keyHasPredefinedValue)
{
return (TKey)idProps.First().GetValue(entityToInsert, null);
}
return (TKey)r.First().id;
}
-
+
///
/// Updates a record or records in the database with only the properties of TEntity
/// By default updates records in the table matching the class name
@@ -451,25 +461,25 @@ public static int Update(this IDbConnection connection, TEntity entityT
StringBuilderCache(masterSb, $"{typeof(TEntity).FullName}_Update", sb =>
{
var idProps = GetIdProperties(entityToUpdate).ToList();
-
+
if (!idProps.Any())
throw new ArgumentException("Entity must have at least one [Key] or Id property");
-
+
var name = GetTableName(entityToUpdate);
-
+
sb.AppendFormat("update {0}", name);
-
+
sb.AppendFormat(" set ");
BuildUpdateSet(entityToUpdate, sb);
sb.Append(" where ");
BuildWhere(sb, idProps, entityToUpdate);
-
+
if (Debugger.IsAttached)
Trace.WriteLine(String.Format("Update: {0}", sb));
});
return connection.Execute(masterSb.ToString(), entityToUpdate, transaction, commandTimeout);
}
-
+
///
/// Deletes a record or records in the database that match the object passed in
/// -By default deletes records in the table matching the class name
@@ -488,25 +498,25 @@ public static int Delete(this IDbConnection connection, T entityToDelete, IDb
var masterSb = new StringBuilder();
StringBuilderCache(masterSb, $"{typeof(T).FullName}_Delete", sb =>
{
-
+
var idProps = GetIdProperties(entityToDelete).ToList();
-
+
if (!idProps.Any())
throw new ArgumentException("Entity must have at least one [Key] or Id property");
-
+
var name = GetTableName(entityToDelete);
-
+
sb.AppendFormat("delete from {0}", name);
-
+
sb.Append(" where ");
BuildWhere(sb, idProps, entityToDelete);
-
+
if (Debugger.IsAttached)
Trace.WriteLine(String.Format("Delete: {0}", sb));
});
return connection.Execute(masterSb.ToString(), entityToDelete, transaction, commandTimeout);
}
-
+
///
/// Deletes a record or records in the database by ID
/// By default deletes records in the table matching the class name
@@ -525,23 +535,23 @@ public static int Delete(this IDbConnection connection, object id, IDbTransac
{
var currenttype = typeof(T);
var idProps = GetIdProperties(currenttype).ToList();
-
-
+
+
if (!idProps.Any())
throw new ArgumentException("Delete only supports an entity with a [Key] or Id property");
-
+
var name = GetTableName(currenttype);
-
+
var sb = new StringBuilder();
sb.AppendFormat("Delete from {0} where ", name);
-
+
for (var i = 0; i < idProps.Count; i++)
{
if (i > 0)
sb.Append(" and ");
sb.AppendFormat("{0} = @{1}", GetColumnName(idProps[i]), idProps[i].Name);
}
-
+
var dynParms = new DynamicParameters();
if (idProps.Count == 1)
dynParms.Add("@" + idProps.First().Name, id);
@@ -550,13 +560,13 @@ public static int Delete(this IDbConnection connection, object id, IDbTransac
foreach (var prop in idProps)
dynParms.Add("@" + prop.Name, id.GetType().GetProperty(prop.Name).GetValue(id, null));
}
-
+
if (Debugger.IsAttached)
Trace.WriteLine(String.Format("Delete<{0}> {1}", currenttype, sb));
-
+
return connection.Execute(sb.ToString(), dynParms, transaction, commandTimeout);
}
-
+
///
/// Deletes a list of records in the database
/// By default deletes records in the table matching the class name
@@ -579,7 +589,7 @@ public static int DeleteList(this IDbConnection connection, object whereCondi
{
var currenttype = typeof(T);
var name = GetTableName(currenttype);
-
+
var whereprops = GetAllProperties(whereConditions).ToArray();
sb.AppendFormat("Delete from {0}", name);
if (whereprops.Any())
@@ -587,13 +597,13 @@ public static int DeleteList(this IDbConnection connection, object whereCondi
sb.Append(" where ");
BuildWhere(sb, whereprops);
}
-
+
if (Debugger.IsAttached)
Trace.WriteLine(String.Format("DeleteList<{0}> {1}", currenttype, sb));
});
return connection.Execute(masterSb.ToString(), whereConditions, transaction, commandTimeout);
}
-
+
///
/// Deletes a list of records in the database
/// By default deletes records in the table matching the class name
@@ -619,19 +629,19 @@ public static int DeleteList(this IDbConnection connection, string conditions
throw new ArgumentException("DeleteList requires a where clause");
if (!conditions.ToLower().Contains("where"))
throw new ArgumentException("DeleteList requires a where clause and must contain the WHERE keyword");
-
+
var currenttype = typeof(T);
var name = GetTableName(currenttype);
-
+
sb.AppendFormat("Delete from {0}", name);
sb.Append(" " + conditions);
-
+
if (Debugger.IsAttached)
Trace.WriteLine(String.Format("DeleteList<{0}> {1}", currenttype, sb));
});
return connection.Execute(masterSb.ToString(), parameters, transaction, commandTimeout);
}
-
+
///
/// By default queries the table matching the class name
/// -Table name can be overridden by adding an attribute on your class [Table("YourTableName")]
@@ -655,13 +665,13 @@ public static int RecordCount(this IDbConnection connection, string condition
sb.Append("Select count(1)");
sb.AppendFormat(" from {0}", name);
sb.Append(" " + conditions);
-
+
if (Debugger.IsAttached)
Trace.WriteLine(String.Format("RecordCount<{0}>: {1}", currenttype, sb));
-
+
return connection.ExecuteScalar(sb.ToString(), parameters, transaction, commandTimeout);
}
-
+
///
/// By default queries the table matching the class name
/// -Table name can be overridden by adding an attribute on your class [Table("YourTableName")]
@@ -679,7 +689,7 @@ public static int RecordCount(this IDbConnection connection, object whereCond
{
var currenttype = typeof(T);
var name = GetTableName(currenttype);
-
+
var sb = new StringBuilder();
var whereprops = GetAllProperties(whereConditions).ToArray();
sb.Append("Select count(1)");
@@ -689,31 +699,31 @@ public static int RecordCount(this IDbConnection connection, object whereCond
sb.Append(" where ");
BuildWhere(sb, whereprops);
}
-
+
if (Debugger.IsAttached)
Trace.WriteLine(String.Format("RecordCount<{0}>: {1}", currenttype, sb));
-
+
return connection.ExecuteScalar(sb.ToString(), whereConditions, transaction, commandTimeout);
}
-
+
//build update statement based on list on an entity
private static void BuildUpdateSet(T entityToUpdate, StringBuilder masterSb)
{
StringBuilderCache(masterSb, $"{typeof(T).FullName}_BuildUpdateSet", sb =>
{
var nonIdProps = GetUpdateableProperties(entityToUpdate).ToArray();
-
+
for (var i = 0; i < nonIdProps.Length; i++)
{
var property = nonIdProps[i];
-
+
sb.AppendFormat("{0} = @{1}", GetColumnName(property), property.Name);
if (i < nonIdProps.Length - 1)
sb.AppendFormat(", ");
}
});
}
-
+
//build select clause based on list of properties skipping ones with the IgnoreSelect and NotMapped attribute
private static void BuildSelect(StringBuilder masterSb, IEnumerable props)
{
@@ -724,27 +734,29 @@ private static void BuildSelect(StringBuilder masterSb, IEnumerable attr.GetType().Name == typeof(IgnoreSelectAttribute).Name || attr.GetType().Name == typeof(NotMappedAttribute).Name)) continue;
-
+ var colProperties = _columnPropertiesResolver.ResolveColumnProperties(property);
+
+ if (colProperties.IsIgnoredInSelect || colProperties.IsNotMapped) continue;
+
if (addedAny)
sb.Append(",");
- sb.Append(GetColumnName(property));
+
+ sb.Append(GetColumnName(property)); // use Encapsulate(colProperties.ColumnName) when _columnNameResolver is dropped
//if there is a custom column name add an "as customcolumnname" to the item so it maps properly
- if (property.GetCustomAttributes(true).SingleOrDefault(attr => attr.GetType().Name == typeof(ColumnAttribute).Name) != null)
- sb.Append(" as " + Encapsulate(property.Name));
+ if (!string.IsNullOrEmpty(colProperties.AliasName))
+ sb.Append(" as " + Encapsulate(colProperties.AliasName));
addedAny = true;
}
});
}
-
+
private static void BuildWhere(StringBuilder sb, IEnumerable idProps, object whereConditions = null)
{
var propertyInfos = idProps.ToArray();
for (var i = 0; i < propertyInfos.Count(); i++)
{
var useIsNull = false;
-
+
//match up generic properties to source entity properties to allow fetching of the column attribute
//the anonymous object used for search doesn't have the custom attributes attached to them so this allows us to build the correct where clause
//by converting the model type to the database column name via the column attribute
@@ -766,12 +778,12 @@ private static void BuildWhere(StringBuilder sb, IEnumerable(StringBuilder masterSb)
{
StringBuilderCache(masterSb, $"{typeof(T).FullName}_BuildInsertValues", sb =>
{
-
+
var props = GetScaffoldableProperties().ToArray();
for (var i = 0; i < props.Count(); i++)
{
var property = props.ElementAt(i);
+ var colProperties = _columnPropertiesResolver.ResolveColumnProperties(property);
if (property.PropertyType != typeof(Guid) && property.PropertyType != typeof(string)
- && property.GetCustomAttributes(true).Any(attr => attr.GetType().Name == typeof(KeyAttribute).Name)
- && property.GetCustomAttributes(true).All(attr => attr.GetType().Name != typeof(RequiredAttribute).Name))
+ && colProperties.IsKey && !colProperties.IsRequired)
continue;
- if (property.GetCustomAttributes(true).Any(attr =>
- attr.GetType().Name == typeof(IgnoreInsertAttribute).Name ||
- attr.GetType().Name == typeof(NotMappedAttribute).Name ||
- attr.GetType().Name == typeof(ReadOnlyAttribute).Name && IsReadOnly(property))
- ) continue;
-
- if (property.Name.Equals("Id", StringComparison.OrdinalIgnoreCase) && property.GetCustomAttributes(true).All(attr => attr.GetType().Name != typeof(RequiredAttribute).Name) && property.PropertyType != typeof(Guid)) continue;
-
+
+ if (colProperties.IsIgnoredInInsert || colProperties.IsNotMapped || colProperties.IsReadOnly)
+ continue;
+
+ if (property.Name.Equals("Id", StringComparison.OrdinalIgnoreCase) && !colProperties.IsRequired && property.PropertyType != typeof(Guid))
+ continue;
+
sb.AppendFormat("@{0}", property.Name);
if (i < props.Count() - 1)
sb.Append(", ");
@@ -807,7 +818,7 @@ private static void BuildInsertValues(StringBuilder masterSb)
sb.Remove(sb.Length - 2, 2);
});
}
-
+
//build insert parameters which include all properties in the class that are not:
//marked with the Editable(false) attribute
//marked with the [Key] attribute
@@ -819,22 +830,23 @@ private static void BuildInsertParameters(StringBuilder masterSb)
StringBuilderCache(masterSb, $"{typeof(T).FullName}_BuildInsertParameters", sb =>
{
var props = GetScaffoldableProperties().ToArray();
-
+
for (var i = 0; i < props.Count(); i++)
{
var property = props.ElementAt(i);
+ var colProperties = _columnPropertiesResolver.ResolveColumnProperties(property);
+
if (property.PropertyType != typeof(Guid) && property.PropertyType != typeof(string)
- && property.GetCustomAttributes(true).Any(attr => attr.GetType().Name == typeof(KeyAttribute).Name)
- && property.GetCustomAttributes(true).All(attr => attr.GetType().Name != typeof(RequiredAttribute).Name))
+ && colProperties.IsKey && !colProperties.IsRequired)
continue;
- if (property.GetCustomAttributes(true).Any(attr =>
- attr.GetType().Name == typeof(IgnoreInsertAttribute).Name ||
- attr.GetType().Name == typeof(NotMappedAttribute).Name ||
- attr.GetType().Name == typeof(ReadOnlyAttribute).Name && IsReadOnly(property))) continue;
-
- if (property.Name.Equals("Id", StringComparison.OrdinalIgnoreCase) && property.GetCustomAttributes(true).All(attr => attr.GetType().Name != typeof(RequiredAttribute).Name) && property.PropertyType != typeof(Guid)) continue;
-
- sb.Append(GetColumnName(property));
+
+ if (colProperties.IsIgnoredInInsert || colProperties.IsNotMapped || colProperties.IsReadOnly)
+ continue;
+
+ if (property.Name.Equals("Id", StringComparison.OrdinalIgnoreCase) && !colProperties.IsRequired && property.PropertyType != typeof(Guid))
+ continue;
+
+ sb.Append(Encapsulate(colProperties.ColumnName));
if (i < props.Count() - 1)
sb.Append(", ");
}
@@ -849,53 +861,15 @@ private static IEnumerable GetAllProperties(T entity) where T :
if (entity == null) return new PropertyInfo[0];
return entity.GetType().GetProperties();
}
-
+
//Get all properties that are not decorated with the Editable(false) attribute
private static IEnumerable GetScaffoldableProperties()
{
IEnumerable props = typeof(T).GetProperties();
-
- props = props.Where(p => p.GetCustomAttributes(true).Any(attr => attr.GetType().Name == typeof(EditableAttribute).Name && !IsEditable(p)) == false);
-
-
- return props.Where(p => p.PropertyType.IsSimpleType() || IsEditable(p));
+
+ return props.Where(p => _columnPropertiesResolver.ResolveColumnProperties(p).IsEditable);
}
-
- //Determine if the Attribute has an AllowEdit key and return its boolean state
- //fake the funk and try to mimic EditableAttribute in System.ComponentModel.DataAnnotations
- //This allows use of the DataAnnotations property in the model and have the SimpleCRUD engine just figure it out without a reference
- private static bool IsEditable(PropertyInfo pi)
- {
- var attributes = pi.GetCustomAttributes(false);
- if (attributes.Length > 0)
- {
- dynamic write = attributes.FirstOrDefault(x => x.GetType().Name == typeof(EditableAttribute).Name);
- if (write != null)
- {
- return write.AllowEdit;
- }
- }
- return false;
- }
-
-
- //Determine if the Attribute has an IsReadOnly key and return its boolean state
- //fake the funk and try to mimic ReadOnlyAttribute in System.ComponentModel
- //This allows use of the DataAnnotations property in the model and have the SimpleCRUD engine just figure it out without a reference
- private static bool IsReadOnly(PropertyInfo pi)
- {
- var attributes = pi.GetCustomAttributes(false);
- if (attributes.Length > 0)
- {
- dynamic write = attributes.FirstOrDefault(x => x.GetType().Name == typeof(ReadOnlyAttribute).Name);
- if (write != null)
- {
- return write.IsReadOnly;
- }
- }
- return false;
- }
-
+
//Get all properties that are:
//Not named Id
//Not marked with the Key attribute
@@ -905,20 +879,17 @@ private static bool IsReadOnly(PropertyInfo pi)
private static IEnumerable GetUpdateableProperties(T entity)
{
var updateableProperties = GetScaffoldableProperties();
- //remove ones with ID
- updateableProperties = updateableProperties.Where(p => !p.Name.Equals("Id", StringComparison.OrdinalIgnoreCase));
- //remove ones with key attribute
- updateableProperties = updateableProperties.Where(p => p.GetCustomAttributes(true).Any(attr => attr.GetType().Name == typeof(KeyAttribute).Name) == false);
- //remove ones that are readonly
- updateableProperties = updateableProperties.Where(p => p.GetCustomAttributes(true).Any(attr => (attr.GetType().Name == typeof(ReadOnlyAttribute).Name) && IsReadOnly(p)) == false);
- //remove ones with IgnoreUpdate attribute
- updateableProperties = updateableProperties.Where(p => p.GetCustomAttributes(true).Any(attr => attr.GetType().Name == typeof(IgnoreUpdateAttribute).Name) == false);
- //remove ones that are not mapped
- updateableProperties = updateableProperties.Where(p => p.GetCustomAttributes(true).Any(attr => attr.GetType().Name == typeof(NotMappedAttribute).Name) == false);
-
- return updateableProperties;
+ return updateableProperties.Where(p => {
+ var cp = _columnPropertiesResolver.ResolveColumnProperties(p);
+ //remove ones with ID
+ return !p.Name.Equals("Id", StringComparison.OrdinalIgnoreCase) &&
+ !cp.IsKey &&
+ !cp.IsReadOnly &&
+ !cp.IsIgnoredInUpdate &&
+ !cp.IsNotMapped;
+ });
}
-
+
//Get all properties that are named Id or have the Key attribute
//For Inserts and updates we have a whole entity so this method is used
private static IEnumerable GetIdProperties(object entity)
@@ -926,15 +897,15 @@ private static IEnumerable GetIdProperties(object entity)
var type = entity.GetType();
return GetIdProperties(type);
}
-
+
//Get all properties that are named Id or have the Key attribute
//For Get(id) and Delete(id) we don't have an entity, just the type so this method is used
private static IEnumerable GetIdProperties(Type type)
{
- var tp = type.GetProperties().Where(p => p.GetCustomAttributes(true).Any(attr => attr.GetType().Name == typeof(KeyAttribute).Name)).ToList();
+ var tp = type.GetProperties().Where(p => _columnPropertiesResolver.ResolveColumnProperties(p).IsKey).ToList();
return tp.Any() ? tp : type.GetProperties().Where(p => p.Name.Equals("Id", StringComparison.OrdinalIgnoreCase));
}
-
+
//Gets the table name for this entity
//For Inserts and updates we have a whole entity so this method is used
//Uses class name by default and overrides if the class has a Table attribute
@@ -943,40 +914,22 @@ private static string GetTableName(object entity)
var type = entity.GetType();
return GetTableName(type);
}
-
+
//Gets the table name for this type
//For Get(id) and Delete(id) we don't have an entity, just the type so this method is used
//Use dynamic type to be able to handle both our Table-attribute and the DataAnnotation
//Uses class name by default and overrides if the class has a Table attribute
private static string GetTableName(Type type)
{
- string tableName;
-
- if (TableNames.TryGetValue(type, out tableName))
- return tableName;
-
- tableName = _tableNameResolver.ResolveTableName(type);
-
- TableNames.AddOrUpdate(type, tableName, (t, v) => tableName);
-
- return tableName;
+ return _tableNameResolver.ResolveTableName(type);
}
-
+
private static string GetColumnName(PropertyInfo propertyInfo)
- {
- string columnName, key = string.Format("{0}.{1}", propertyInfo.DeclaringType, propertyInfo.Name);
-
- if (ColumnNames.TryGetValue(key, out columnName))
- return columnName;
-
- columnName = _columnNameResolver.ResolveColumnName(propertyInfo);
-
- ColumnNames.AddOrUpdate(key, columnName, (t, v) => columnName);
-
- return columnName;
+ {
+ return Encapsulate(_columnNameResolver.ResolveColumnName(propertyInfo));
}
-
- private static string Encapsulate(string databaseword)
+
+ public static string Encapsulate(string databaseword)
{
return string.Format(_encapsulation, databaseword);
}
@@ -998,7 +951,7 @@ public static Guid SequentialGuid()
bytes[4] = (byte)time.Second;
return new Guid(bytes);
}
-
+
///
/// Database server dialects
///
@@ -1011,23 +964,65 @@ public enum Dialect
Oracle,
DB2
}
-
+
public interface ITableNameResolver
{
string ResolveTableName(Type type);
}
-
+
+ [Obsolete("Implement IColumnPropertiesResolver instead.")]
public interface IColumnNameResolver
{
string ResolveColumnName(PropertyInfo propertyInfo);
}
-
- public class TableNameResolver : ITableNameResolver
+
+ public interface IColumnPropertiesResolver
{
+ IColumnProperties ResolveColumnProperties(PropertyInfo propertyInfo);
+ }
+
+ public interface IColumnProperties
+ {
+ string AliasName { get; }
+ string ColumnName { get; }
+ bool IsKey { get; }
+ bool IsRequired { get; }
+ bool IsEditable { get; }
+ bool IsNotMapped { get; }
+ bool IsReadOnly { get; }
+ bool IsIgnoredInSelect { get; }
+ bool IsIgnoredInInsert { get; }
+ bool IsIgnoredInUpdate { get; }
+ }
+
+ public class ColumnProperties : IColumnProperties
+ {
+ public string AliasName { get; set; }
+ public string ColumnName { get; set; }
+ public bool IsKey { get; set; }
+ public bool IsRequired { get; set; }
+ public bool IsEditable { get; set; }
+ public bool IsNotMapped { get; set; }
+ public bool IsReadOnly { get; set; }
+ public bool IsIgnoredInSelect { get; set; }
+ public bool IsIgnoredInInsert { get; set; }
+ public bool IsIgnoredInUpdate { get; set; }
+ }
+
+ public class TableMapResolver : IColumnPropertiesResolver, ITableNameResolver, IColumnNameResolver
+ {
+ protected readonly Dictionary cacheTableNames = new Dictionary();
+ protected readonly Dictionary cacheColumnsProperties = new Dictionary();
+
public virtual string ResolveTableName(Type type)
{
+ if (cacheTableNames.ContainsKey(type))
+ {
+ return cacheTableNames[type];
+ }
+
string tableName;
-
+
if (GetDialect() == Dialect.DB2.ToString())
{
tableName = type.Name;
@@ -1036,7 +1031,7 @@ public virtual string ResolveTableName(Type type)
{
tableName = Encapsulate(type.Name);
}
-
+
var tableattr = type.GetCustomAttributes(true).SingleOrDefault(attr => attr.GetType().Name == typeof(TableAttribute).Name) as dynamic;
if (tableattr != null)
{
@@ -1054,38 +1049,53 @@ public virtual string ResolveTableName(Type type)
//Schema doesn't exist on this attribute.
}
}
-
+ cacheTableNames.Add(type, tableName);
return tableName;
}
- }
-
- public class ColumnNameResolver : IColumnNameResolver
- {
public virtual string ResolveColumnName(PropertyInfo propertyInfo)
{
- string columnName;
-
- if (GetDialect() == Dialect.DB2.ToString())
- {
- columnName = propertyInfo.Name;
- }
- else
+ return ResolveColumnProperties(propertyInfo).ColumnName;
+ }
+
+ public virtual IColumnProperties ResolveColumnProperties(PropertyInfo propertyInfo)
+ {
+ if (cacheColumnsProperties.ContainsKey(propertyInfo))
{
- columnName = Encapsulate(propertyInfo.Name);
+ return cacheColumnsProperties[propertyInfo];
}
-
- var columnattr = propertyInfo.GetCustomAttributes(true).SingleOrDefault(attr => attr.GetType().Name == typeof(ColumnAttribute).Name) as dynamic;
- if (columnattr != null)
+
+ var columnName = propertyInfo.Name;
+ string aliasName = null;
+
+ var attributes = propertyInfo.GetCustomAttributes(true);
+ var columnNameAttr = attributes.SingleOrDefault(attr => attr.GetType().Name == typeof(ColumnAttribute).Name) as dynamic;
+ if (columnNameAttr != null)
{
- columnName = Encapsulate(columnattr.Name);
+ aliasName = columnName;
+ columnName = columnNameAttr.Name;
if (Debugger.IsAttached)
Trace.WriteLine(String.Format("Column name for type overridden from {0} to {1}", propertyInfo.Name, columnName));
}
- return columnName;
+
+ var colProperties = new ColumnProperties
+ {
+ AliasName = aliasName,
+ ColumnName = columnName,
+ IsKey = attributes.Any(attr => attr.GetType().Name == typeof(KeyAttribute).Name),
+ IsRequired = attributes.Any(attr => attr.GetType().Name == typeof(RequiredAttribute).Name),
+ IsEditable = (attributes.FirstOrDefault(attr => attr.GetType().Name == typeof(EditableAttribute).Name) as dynamic)?.AllowEdit ?? propertyInfo.PropertyType.IsSimpleType(),
+ IsReadOnly = (attributes.FirstOrDefault(attr => attr.GetType().Name == typeof(ReadOnlyAttribute).Name) as dynamic)?.IsReadOnly ?? false,
+ IsIgnoredInSelect = attributes.Any(attr => attr.GetType().Name == typeof(IgnoreSelectAttribute).Name),
+ IsIgnoredInInsert = attributes.Any(attr => attr.GetType().Name == typeof(IgnoreInsertAttribute).Name),
+ IsIgnoredInUpdate = attributes.Any(attr => attr.GetType().Name == typeof(IgnoreUpdateAttribute).Name),
+ IsNotMapped = attributes.Any(attr => attr.GetType().Name == typeof(NotMappedAttribute).Name),
+ };
+ cacheColumnsProperties.Add(propertyInfo, colProperties);
+ return colProperties;
}
}
}
-
+
///
/// Optional Table attribute.
/// You can use the System.ComponentModel.DataAnnotations version in its place to specify the table name of a poco
@@ -1110,7 +1120,7 @@ public TableAttribute(string tableName)
///
public string Schema { get; set; }
}
-
+
///
/// Optional Column attribute.
/// You can use the System.ComponentModel.DataAnnotations version in its place to specify the table name of a poco
@@ -1131,7 +1141,7 @@ public ColumnAttribute(string columnName)
///
public string Name { get; private set; }
}
-
+
///
/// Optional Key attribute.
/// You can use the System.ComponentModel.DataAnnotations version in its place to specify the Primary Key of a poco
@@ -1140,7 +1150,7 @@ public ColumnAttribute(string columnName)
public class KeyAttribute : Attribute
{
}
-
+
///
/// Optional NotMapped attribute.
/// You can use the System.ComponentModel.DataAnnotations version in its place to specify that the property is not mapped
@@ -1149,7 +1159,7 @@ public class KeyAttribute : Attribute
public class NotMappedAttribute : Attribute
{
}
-
+
///
/// Optional Key attribute.
/// You can use the System.ComponentModel.DataAnnotations version in its place to specify a required property of a poco
@@ -1158,7 +1168,7 @@ public class NotMappedAttribute : Attribute
public class RequiredAttribute : Attribute
{
}
-
+
///
/// Optional Editable attribute.
/// You can use the System.ComponentModel.DataAnnotations version in its place to specify the properties that are editable
@@ -1179,7 +1189,7 @@ public EditableAttribute(bool iseditable)
///
public bool AllowEdit { get; private set; }
}
-
+
///
/// Optional Readonly attribute.
/// You can use the System.ComponentModel version in its place to specify the properties that are editable
@@ -1200,7 +1210,7 @@ public ReadOnlyAttribute(bool isReadOnly)
///
public bool IsReadOnly { get; private set; }
}
-
+
///
/// Optional IgnoreSelect attribute.
/// Custom for Dapper.SimpleCRUD to exclude a property from Select methods
@@ -1209,7 +1219,7 @@ public ReadOnlyAttribute(bool isReadOnly)
public class IgnoreSelectAttribute : Attribute
{
}
-
+
///
/// Optional IgnoreInsert attribute.
/// Custom for Dapper.SimpleCRUD to exclude a property from Insert methods
@@ -1218,7 +1228,7 @@ public class IgnoreSelectAttribute : Attribute
public class IgnoreInsertAttribute : Attribute
{
}
-
+
///
/// Optional IgnoreUpdate attribute.
/// Custom for Dapper.SimpleCRUD to exclude a property from Update methods
@@ -1227,9 +1237,9 @@ public class IgnoreInsertAttribute : Attribute
public class IgnoreUpdateAttribute : Attribute
{
}
-
+
}
-
+
internal static class TypeExtension
{
//You can't insert or update complex types. Lets filter them out.
@@ -1261,7 +1271,7 @@ public static bool IsSimpleType(this Type type)
};
return simpleTypes.Contains(type) || type.IsEnum;
}
-
+
public static string CacheKey(this IEnumerable props)
{
return string.Join(",",props.Select(p=> p.DeclaringType.FullName + "." + p.Name).ToArray());
diff --git a/Dapper.SimpleCRUDFluentTableMap.Tests/Assert.cs b/Dapper.SimpleCRUDFluentTableMap.Tests/Assert.cs
new file mode 100644
index 0000000..609a6e2
--- /dev/null
+++ b/Dapper.SimpleCRUDFluentTableMap.Tests/Assert.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Dapper.SimpleCRUDFluentTableMapTests
+{
+ ///
+ /// Assert extensions borrowed from Sam's code in DapperTests
+ ///
+ static class Assert
+ {
+ public static void IsEqualTo(this T obj, T other)
+ {
+ if (!obj.Equals(other))
+ {
+ throw new ApplicationException(string.Format("{0} should be equal to {1}", obj, other));
+ }
+ }
+
+ public static void IsSequenceEqualTo(this IEnumerable obj, IEnumerable other)
+ {
+ if (!obj.SequenceEqual(other))
+ {
+ throw new ApplicationException(string.Format("{0} should be equal to {1}", obj, other));
+ }
+ }
+
+ public static void IsFalse(this bool b)
+ {
+ if (b)
+ {
+ throw new ApplicationException("Expected false");
+ }
+ }
+
+ public static void IsTrue(this bool b)
+ {
+ if (!b)
+ {
+ throw new ApplicationException("Expected true");
+ }
+ }
+
+ public static void IsNull(this object obj)
+ {
+ if (obj != null)
+ {
+ throw new ApplicationException("Expected null");
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/Dapper.SimpleCRUDFluentTableMap.Tests/Dapper.SimpleCRUDFluentTableMap.Tests.csproj b/Dapper.SimpleCRUDFluentTableMap.Tests/Dapper.SimpleCRUDFluentTableMap.Tests.csproj
new file mode 100644
index 0000000..2b6a2c3
--- /dev/null
+++ b/Dapper.SimpleCRUDFluentTableMap.Tests/Dapper.SimpleCRUDFluentTableMap.Tests.csproj
@@ -0,0 +1,43 @@
+
+
+
+ Exe
+ netcoreapp3.1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ NETCORE;NETSTANDARD;NETSTANDARD2_1
+
+
+
+ NET472;NETFULL
+
+
+
diff --git a/Dapper.SimpleCRUDFluentTableMap.Tests/Program.cs b/Dapper.SimpleCRUDFluentTableMap.Tests/Program.cs
new file mode 100644
index 0000000..fed2640
--- /dev/null
+++ b/Dapper.SimpleCRUDFluentTableMap.Tests/Program.cs
@@ -0,0 +1,321 @@
+using System;
+using System.Data.SqlClient;
+using System.Data.SQLite;
+using System.Diagnostics;
+using System.IO;
+using System.Reflection;
+using IBM.Data.DB2.Core;
+using MySql.Data.MySqlClient;
+using Npgsql;
+
+namespace Dapper.SimpleCRUDFluentTableMapTests
+{
+ class Program
+ {
+ public const string SQLServerName = @".\sqlexpress";
+
+ static void Main()
+ {
+ Setup();
+ RunTests();
+
+ SetupSqLite();
+ RunTestsSqLite();
+
+ //PostgreSQL tests assume port 5432 with username postgres and password postgrespass
+ //they are commented out by default since postgres setup is required to run tests
+ //SetupPg();
+ //RunTestsPg();
+
+ //MySQL tests assume port 3306 with username admin and password admin
+ //they are commented out by default since mysql setup is required to run tests
+ //SetupMySQL();
+ //RunTestsMySQL();
+
+ //DB2 tests assume port 50000 with username db2admin and password db2admin
+ //they are commented out by default since db2 setup is required to run tests
+ //SetupDB2();
+ //RunTestsDB2();
+ }
+
+ private static void Setup()
+ {
+ using (var connection = new SqlConnection($@"Data Source={SQLServerName};Initial Catalog=Master;Integrated Security=True"))
+ {
+ connection.Open();
+ try
+ {
+ connection.Execute(@" DROP DATABASE DapperSimpleCrudTestDb; ");
+ }
+ catch (Exception)
+ { }
+
+ connection.Execute(@" CREATE DATABASE DapperSimpleCrudTestDb; ");
+ }
+
+ using (var connection = new SqlConnection($@"Data Source ={SQLServerName};Initial Catalog=DapperSimpleCrudTestDb;Integrated Security=True"))
+ {
+ connection.Open();
+ connection.Execute(@" create table Users (Id int IDENTITY(1,1) not null, Name nvarchar(100) not null, Age int not null, ScheduledDayOff int null, CreatedDate datetime DEFAULT(getdate())) ");
+ connection.Execute(@" create table Car (CarId int IDENTITY(1,1) not null, Id int null, Make nvarchar(100) not null, Model nvarchar(100) not null) ");
+ connection.Execute(@" create table BigCar (CarId bigint IDENTITY(2147483650,1) not null, Make nvarchar(100) not null, Model nvarchar(100) not null) ");
+ connection.Execute(@" create table City (Name nvarchar(100) not null, Population int not null) ");
+ connection.Execute(@" CREATE SCHEMA Log; ");
+ connection.Execute(@" create table Log.CarLog (Id int IDENTITY(1,1) not null, LogNotes nvarchar(100) NOT NULL) ");
+ connection.Execute(@" CREATE TABLE [dbo].[GUIDTest]([Id] [uniqueidentifier] NOT NULL,[name] [varchar](50) NOT NULL, CONSTRAINT [PK_GUIDTest] PRIMARY KEY CLUSTERED ([Id] ASC))");
+ connection.Execute(@" create table StrangeColumnNames (ItemId int IDENTITY(1,1) not null Primary Key, word nvarchar(100) not null, colstringstrangeword nvarchar(100) not null, KeywordedProperty nvarchar(100) null)");
+ connection.Execute(@" create table UserWithoutAutoIdentity (Id int not null Primary Key, Name nvarchar(100) not null, Age int not null) ");
+ connection.Execute(@" create table IgnoreColumns (Id int IDENTITY(1,1) not null Primary Key, IgnoreInsert nvarchar(100) null, IgnoreUpdate nvarchar(100) null, IgnoreSelect nvarchar(100) null, IgnoreAll nvarchar(100) null) ");
+ connection.Execute(@" CREATE TABLE GradingScale ([ScaleID] [int] IDENTITY(1,1) NOT NULL, [AppID] [int] NULL, [ScaleName] [nvarchar](50) NOT NULL, [IsDefault] [bit] NOT NULL)");
+ connection.Execute(@" CREATE TABLE KeyMaster ([Key1] [int] NOT NULL, [Key2] [int] NOT NULL, CONSTRAINT [PK_KeyMaster] PRIMARY KEY CLUSTERED ([Key1] ASC, [Key2] ASC))");
+ connection.Execute(@" CREATE TABLE [dbo].[stringtest]([stringkey] [varchar](50) NOT NULL,[name] [varchar](50) NOT NULL, CONSTRAINT [PK_stringkey] PRIMARY KEY CLUSTERED ([stringkey] ASC))");
+
+ }
+ Console.WriteLine("Created database");
+ }
+
+ private static void SetupPg()
+ {
+ using (var connection = new NpgsqlConnection(String.Format("Server={0};Port={1};User Id={2};Password={3};Database={4};", "localhost", "5432", "postgres", "postgrespass", "postgres")))
+ {
+ connection.Open();
+ // drop database
+ connection.Execute("DROP DATABASE IF EXISTS testdb;");
+ connection.Execute("CREATE DATABASE testdb WITH OWNER = postgres ENCODING = 'UTF8' CONNECTION LIMIT = -1;");
+ }
+ System.Threading.Thread.Sleep(1000);
+
+ using (var connection = new NpgsqlConnection(String.Format("Server={0};Port={1};User Id={2};Password={3};Database={4};", "localhost", "5432", "postgres", "postgrespass", "testdb")))
+ {
+ connection.Open();
+ connection.Execute(@" create table Users (Id SERIAL PRIMARY KEY, Name varchar not null, Age int not null, ScheduledDayOff int null, CreatedDate date not null default CURRENT_DATE) ");
+ connection.Execute(@" create table Car (CarId SERIAL PRIMARY KEY, Id int null, Make varchar not null, Model varchar not null) ");
+ connection.Execute(@" create table BigCar (CarId BIGSERIAL PRIMARY KEY, Make varchar not null, Model varchar not null) ");
+ connection.Execute(@" alter sequence bigcar_carid_seq RESTART WITH 2147483650");
+ connection.Execute(@" create table City (Name varchar not null, Population int not null) ");
+ connection.Execute(@" CREATE SCHEMA Log; ");
+ connection.Execute(@" create table Log.CarLog (Id SERIAL PRIMARY KEY, LogNotes varchar NOT NULL) ");
+ connection.Execute(@" CREATE TABLE GUIDTest(Id uuid PRIMARY KEY,name varchar NOT NULL)");
+ connection.Execute(@" create table StrangeColumnNames (ItemId Serial PRIMARY KEY, word varchar not null, colstringstrangeword varchar, keywordedproperty varchar) ");
+ connection.Execute(@" create table UserWithoutAutoIdentity (Id int PRIMARY KEY, Name varchar not null, Age int not null) ");
+
+ }
+
+ }
+
+ private static void SetupSqLite()
+ {
+ File.Delete(Directory.GetCurrentDirectory() + "\\MyDatabase.sqlite");
+ SQLiteConnection.CreateFile("MyDatabase.sqlite");
+ var connection = new SQLiteConnection("Data Source=MyDatabase.sqlite;Version=3;");
+ using (connection)
+ {
+ connection.Open();
+ connection.Execute(@" create table Users (Id INTEGER PRIMARY KEY AUTOINCREMENT, Name nvarchar(100) not null, Age int not null, ScheduledDayOff int null, CreatedDate datetime default current_timestamp ) ");
+ connection.Execute(@" create table Car (CarId INTEGER PRIMARY KEY AUTOINCREMENT, Id INTEGER null, Make nvarchar(100) not null, Model nvarchar(100) not null) ");
+ connection.Execute(@" create table BigCar (CarId INTEGER PRIMARY KEY AUTOINCREMENT, Make nvarchar(100) not null, Model nvarchar(100) not null) ");
+ connection.Execute(@" insert into BigCar (CarId,Make,Model) Values (2147483649,'car','car') ");
+ connection.Execute(@" create table City (Name nvarchar(100) not null, Population int not null) ");
+ connection.Execute(@" CREATE TABLE GUIDTest([Id] [uniqueidentifier] NOT NULL,[name] [varchar](50) NOT NULL, CONSTRAINT [PK_GUIDTest] PRIMARY KEY ([Id] ASC))");
+ connection.Execute(@" create table StrangeColumnNames (ItemId INTEGER PRIMARY KEY AUTOINCREMENT, word nvarchar(100) not null, colstringstrangeword nvarchar(100) not null, KeywordedProperty nvarchar(100) null) ");
+ connection.Execute(@" create table UserWithoutAutoIdentity (Id INTEGER PRIMARY KEY, Name nvarchar(100) not null, Age int not null) ");
+ connection.Execute(@" create table IgnoreColumns (Id INTEGER PRIMARY KEY AUTOINCREMENT, IgnoreInsert nvarchar(100) null, IgnoreUpdate nvarchar(100) null, IgnoreSelect nvarchar(100) null, IgnoreAll nvarchar(100) null) ");
+ connection.Execute(@" CREATE TABLE KeyMaster (Key1 INTEGER NOT NULL, Key2 INTEGER NOT NULL, PRIMARY KEY ([Key1], [Key2]))");
+ connection.Execute(@" CREATE TABLE stringtest (stringkey nvarchar(50) NOT NULL,name nvarchar(50) NOT NULL, PRIMARY KEY ([stringkey] ASC))");
+
+ }
+ }
+
+ private static void SetupMySQL()
+ {
+ using (var connection = new MySqlConnection(String.Format("Server={0};Port={1};User Id={2};Password={3};Database={4};", "localhost", "3306", "root", "admin", "sys")))
+ {
+ connection.Open();
+ // drop database
+ connection.Execute("DROP DATABASE IF EXISTS testdb;");
+ connection.Execute("CREATE DATABASE testdb;");
+ }
+ System.Threading.Thread.Sleep(1000);
+
+ using (var connection = new MySqlConnection(String.Format("Server={0};Port={1};User Id={2};Password={3};Database={4};", "localhost", "3306", "root", "admin", "testdb")))
+ {
+ connection.Open();
+ connection.Execute(@" create table Users (Id INTEGER PRIMARY KEY AUTO_INCREMENT, Name nvarchar(100) not null, Age int not null, ScheduledDayOff int null, CreatedDate datetime default current_timestamp ) ");
+ connection.Execute(@" create table Car (CarId INTEGER PRIMARY KEY AUTO_INCREMENT, Id INTEGER null, Make nvarchar(100) not null, Model nvarchar(100) not null) ");
+ connection.Execute(@" create table BigCar (CarId BIGINT PRIMARY KEY AUTO_INCREMENT, Make nvarchar(100) not null, Model nvarchar(100) not null) ");
+ connection.Execute(@" insert into BigCar (CarId,Make,Model) Values (2147483649,'car','car') ");
+ connection.Execute(@" create table City (Name nvarchar(100) not null, Population int not null) ");
+ connection.Execute(@" CREATE TABLE GUIDTest(Id CHAR(38) NOT NULL,name varchar(50) NOT NULL, CONSTRAINT PK_GUIDTest PRIMARY KEY (Id ASC))");
+ connection.Execute(@" create table StrangeColumnNames (ItemId INTEGER PRIMARY KEY AUTO_INCREMENT, word nvarchar(100) not null, colstringstrangeword nvarchar(100) not null, KeywordedProperty nvarchar(100) null) ");
+ connection.Execute(@" create table UserWithoutAutoIdentity (Id INTEGER PRIMARY KEY, Name nvarchar(100) not null, Age int not null) ");
+ connection.Execute(@" create table IgnoreColumns (Id INTEGER PRIMARY KEY AUTO_INCREMENT, IgnoreInsert nvarchar(100) null, IgnoreUpdate nvarchar(100) null, IgnoreSelect nvarchar(100) null, IgnoreAll nvarchar(100) null) ");
+ connection.Execute(@" CREATE table KeyMaster (Key1 INTEGER NOT NULL, Key2 INTEGER NOT NULL, CONSTRAINT PK_KeyMaster PRIMARY KEY CLUSTERED (Key1 ASC, Key2 ASC))");
+ }
+
+ }
+
+
+ private static void RunTests()
+ {
+ var stopwatch = Stopwatch.StartNew();
+ var sqltester = new Tests(SimpleCRUD.Dialect.SQLServer);
+ foreach (var method in typeof(Tests).GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
+ {
+ var testwatch = Stopwatch.StartNew();
+ Console.Write("Running " + method.Name + " in sql server");
+ method.Invoke(sqltester, null);
+ testwatch.Stop();
+ Console.WriteLine(" - OK! {0}ms", testwatch.ElapsedMilliseconds);
+ }
+ stopwatch.Stop();
+
+ // Write result
+ Console.WriteLine("Time elapsed: {0}", stopwatch.Elapsed);
+
+ using (var connection = new SqlConnection($@"Data Source={SQLServerName};Initial Catalog=Master;Integrated Security=True"))
+ {
+ connection.Open();
+ try
+ {
+ //drop any remaining connections, then drop the db.
+ connection.Execute(@" alter database DapperSimpleCrudTestDb set single_user with rollback immediate; DROP DATABASE DapperSimpleCrudTestDb; ");
+ }
+ catch (Exception)
+ { }
+ }
+ Console.Write("SQL Server testing complete.");
+ Console.ReadKey();
+ }
+
+ private static void RunTestsPg()
+ {
+ var stopwatch = Stopwatch.StartNew();
+ var pgtester = new Tests(SimpleCRUD.Dialect.PostgreSQL);
+ foreach (var method in typeof(Tests).GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
+ {
+ var testwatch = Stopwatch.StartNew();
+ Console.Write("Running " + method.Name + " in PostgreSQL");
+ method.Invoke(pgtester, null);
+ Console.WriteLine(" - OK! {0}ms", testwatch.ElapsedMilliseconds);
+ }
+ stopwatch.Stop();
+ Console.WriteLine("Time elapsed: {0}", stopwatch.Elapsed);
+
+ Console.Write("PostgreSQL testing complete.");
+ Console.ReadKey();
+ }
+
+ private static void RunTestsSqLite()
+ {
+ var stopwatch = Stopwatch.StartNew();
+ var pgtester = new Tests(SimpleCRUD.Dialect.SQLite);
+ foreach (var method in typeof(Tests).GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
+ {
+ //skip schema tests
+ if (method.Name.Contains("Schema")) continue;
+ var testwatch = Stopwatch.StartNew();
+ Console.Write("Running " + method.Name + " in SQLite");
+ method.Invoke(pgtester, null);
+ Console.WriteLine(" - OK! {0}ms", testwatch.ElapsedMilliseconds);
+ }
+ stopwatch.Stop();
+ Console.WriteLine("Time elapsed: {0}", stopwatch.Elapsed);
+ Console.Write("SQLite testing complete.");
+ Console.ReadKey();
+ }
+
+ private static void RunTestsMySQL()
+ {
+ var stopwatch = Stopwatch.StartNew();
+ var mysqltester = new Tests(SimpleCRUD.Dialect.MySQL);
+ foreach (var method in typeof(Tests).GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
+ {
+ //skip schema tests
+ if (method.Name.Contains("Schema")) continue;
+ if (method.Name.Contains("Guid")) continue;
+
+ var testwatch = Stopwatch.StartNew();
+ Console.Write("Running " + method.Name + " in MySQL");
+ method.Invoke(mysqltester, null);
+ Console.WriteLine(" - OK! {0}ms", testwatch.ElapsedMilliseconds);
+ }
+ stopwatch.Stop();
+ Console.WriteLine("Time elapsed: {0}", stopwatch.Elapsed);
+
+ Console.Write("MySQL testing complete.");
+ Console.ReadKey();
+ }
+ private static void SetupDB2()
+ {
+ System.Security.SecureString secureString = new System.Security.SecureString();
+ ProcessStartInfo processStartInfo;
+ string user = "db2admin";
+
+ foreach (char c in user)
+ {
+ secureString.AppendChar(c);
+ }
+
+ processStartInfo = new ProcessStartInfo();
+ processStartInfo.FileName = @"db2.exe";
+ processStartInfo.UserName = user;
+ processStartInfo.Password = secureString;
+ processStartInfo.EnvironmentVariables["DB2CLP"] = "DB20FADE";
+ processStartInfo.UseShellExecute = false;
+ processStartInfo.WorkingDirectory = @"C:\Program Files\IBM\SQLLIB\bin";
+
+ foreach (string command in (new string[] { "drop database testdb", "create database testdb" }))
+ {
+ Process process = new Process();
+
+ processStartInfo.Arguments = command;
+
+ process.StartInfo = processStartInfo;
+
+ process.Start();
+ process.WaitForExit();
+ }
+
+ using (var connection = new DB2Connection(string.Format("Server={0};UID={1};PWD={2};Database={3};", "localhost:50000", user, user, "testdb")))
+ {
+ connection.Open();
+
+ // Use the alias for pass the delete test.
+ connection.Execute(@"create table ""Users"" (Id int not null generated by default as identity, Name varchar(100) not null, Age int not null, ScheduledDayOff int, CreatedDate date not null default current_date, primary key(Id));");
+ connection.Execute(@"create alias users for ""Users""");
+ connection.Execute(@"create table Car (CarId int not null generated by default as identity, Id int, Make varchar(100) not null, Model varchar(100) not null, primary key(CarId));");
+ connection.Execute(@"create table BigCar (CarId bigint not null generated by default as identity(start with 2147483650), Make varchar(100) not null, Model varchar(100) not null, primary key(CarId));");
+ connection.Execute(@"create table City (Name varchar(100) not null, Population int not null);");
+ connection.Execute(@"create schema ""Log"";");
+ connection.Execute(@"create table ""Log"".""CarLog"" (Id int not null generated by default as identity, LogNotes varchar(100) not null, primary key(Id));");
+ connection.Execute(@"create table GUIDTest(Id varchar(38) not null, name varchar(50) not null, primary key(Id));");
+ connection.Execute(@"create table StrangeColumnNames (""ItemId"" int not null generated by default as identity, Word varchar(100) not null, ""colstringstrangeword"" varchar(100), ""KeywordedProperty"" varchar(100), primary key(""ItemId""))");
+ connection.Execute(@"create table UserWithoutAutoIdentity (Id int not null generated by default as identity, Name varchar(100) not null, Age int not null, primary key(Id));");
+ connection.Execute(@"create table IgnoreColumns (Id int not null generated by default as identity, IgnoreInsert varchar(100), IgnoreUpdate varchar(100), IgnoreSelect varchar(100), IgnoreAll varchar(100), primary key(Id));");
+ connection.Execute(@"create table KeyMaster (Key1 int not null, Key2 int not null, PRIMARY KEY (Key1, Key2));");
+ connection.Execute(@"create table stringtest (stringkey varchar(50) not null, name varchar(50) not null, primary key(stringkey))");
+ }
+ }
+
+ private static void RunTestsDB2()
+ {
+ var stopwatch = Stopwatch.StartNew();
+ var db2tester = new Tests(SimpleCRUD.Dialect.DB2);
+ foreach (var method in typeof(Tests).GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
+ {
+ //skip schema tests
+ if (method.Name.Contains("Schema")) continue;
+ if (method.Name.Contains("Guid")) continue;
+ var testwatch = Stopwatch.StartNew();
+ Console.Write("Running " + method.Name + " in DB2");
+ method.Invoke(db2tester, null);
+ Console.WriteLine(" - OK! {0}ms", testwatch.ElapsedMilliseconds);
+ }
+ stopwatch.Stop();
+ Console.WriteLine("Time elapsed: {0}", stopwatch.Elapsed);
+
+ Console.Write("DB2 testing complete.");
+ Console.ReadKey();
+ }
+ }
+}
diff --git a/Dapper.SimpleCRUDFluentTableMap.Tests/Tests.cs b/Dapper.SimpleCRUDFluentTableMap.Tests/Tests.cs
new file mode 100644
index 0000000..36ea417
--- /dev/null
+++ b/Dapper.SimpleCRUDFluentTableMap.Tests/Tests.cs
@@ -0,0 +1,1570 @@
+using System.Data;
+using System.Data.SqlClient;
+using System.Linq;
+using System.Collections.Generic;
+using System;
+using System.Data.SQLite;
+using MySql.Data.MySqlClient;
+using Npgsql;
+using IBM.Data.DB2.Core;
+
+namespace Dapper.SimpleCRUDFluentTableMapTests
+{
+ #region DTOClasses
+ public class UserEditableSettingsMapper : SimpleCRUDFluentTableMap.TableMapper
+ {
+ public UserEditableSettingsMapper()
+ {
+ ToTableName("Users");
+ }
+ }
+ public class UserEditableSettings
+ {
+ public int Id { get; set; }
+ public string Name { get; set; }
+ public int Age { get; set; }
+ }
+
+ public class UserMapper : SimpleCRUDFluentTableMap.TableMapper
+ {
+ public UserMapper()
+ {
+ ToTableName("Users");
+ Map(u => u.CreatedDate).AsReadOnly();
+ Map(u => u.NotMappedInt).AsNotMapped();
+ }
+ }
+
+ public class User : UserEditableSettings
+ {
+ //we modified so enums were automatically handled, we should also automatically handle nullable enums
+ public DayOfWeek? ScheduledDayOff { get; set; }
+
+ public DateTime CreatedDate { get; set; }
+
+ public int NotMappedInt { get; set; }
+ }
+
+ public class User1Mapper : SimpleCRUDFluentTableMap.TableMapper
+ {
+ public User1Mapper()
+ {
+ ToTableName("Users");
+ }
+ }
+ public class User1
+ {
+ public int Id { get; set; }
+ public string Name { get; set; }
+ public int Age { get; set; }
+ public int? ScheduledDayOff { get; set; }
+ }
+
+ public class CarMapper : SimpleCRUDFluentTableMap.TableMapper
+ {
+ public CarMapper()
+ {
+ Map(c => c.CarId).AsKey();
+ Map(c => c.MakeWithModel).AsNotEditable();
+ }
+ }
+ public class Car
+ {
+ #region DatabaseFields
+ public int CarId { get; set; }
+ public int? Id { get; set; }
+ public string Make { get; set; }
+ public string Model { get; set; }
+ #endregion
+
+ #region RelatedTables
+ public List Users { get; set; }
+ #endregion
+
+ #region AdditionalFields
+ public string MakeWithModel { get { return Make + " (" + Model + ")"; } }
+ #endregion
+
+ }
+
+ public class BigCarMapper : SimpleCRUDFluentTableMap.TableMapper
+ {
+ public BigCarMapper()
+ {
+ Map(c => c.CarId).AsKey();
+ }
+ }
+ public class BigCar
+ {
+ #region DatabaseFields
+ public long CarId { get; set; }
+ public string Make { get; set; }
+ public string Model { get; set; }
+ #endregion
+
+ }
+
+ public class CarLogMapper : SimpleCRUDFluentTableMap.TableMapper
+ {
+ public CarLogMapper()
+ {
+ ToTableName("Log.CarLog");
+ }
+ }
+
+ public class CarLog
+ {
+ public int Id { get; set; }
+ public string LogNotes { get; set; }
+ }
+
+ ///
+ /// This class should be used for failing tests, since no schema is specified and 'CarLog' is not on dbo
+ ///
+ public class SchemalessCarLogMapper : SimpleCRUDFluentTableMap.TableMapper
+ {
+ public SchemalessCarLogMapper()
+ {
+ ToTableName("CarLog");
+ }
+ }
+
+ public class SchemalessCarLog
+ {
+ public int Id { get; set; }
+ public string LogNotes { get; set; }
+ }
+
+ public class CityMapper : SimpleCRUDFluentTableMap.TableMapper
+ {
+ public CityMapper()
+ {
+ Map(c => c.Name).AsKey();
+ }
+ }
+ public class City
+ {
+ public string Name { get; set; }
+ public int Population { get; set; }
+ }
+
+ public interface INameColumn
+ {
+ string Name { get; set; }
+ }
+
+ public class CityWithINameMapper : SimpleCRUDFluentTableMap.TableMapper
+ {
+ public CityWithINameMapper()
+ {
+ ToTableName("City");
+ Map(c => c.Name).AsKey(); // With FluentTableMap Mapper must be redefined
+ }
+ }
+ public class CityWithIName : City, INameColumn
+ {
+
+ }
+
+ public class UserWithINameMapper : SimpleCRUDFluentTableMap.TableMapper
+ {
+ public UserWithINameMapper()
+ {
+ ToTableName("Users");
+ Map(u => u.CreatedDate).AsReadOnly(); // With FluentTableMap Mapper must be redefined
+ Map(u => u.NotMappedInt).AsNotMapped(); // With FluentTableMap Mapper must be redefined
+ }
+ }
+ public class UserWithIName : User, INameColumn
+ {
+
+ }
+
+ public class GUIDTestMapper : SimpleCRUDFluentTableMap.TableMapper
+ {
+ public GUIDTestMapper()
+ {
+ Map(g => g.Id).AsKey();
+ }
+ }
+ public class GUIDTest
+ {
+ public Guid Id { get; set; }
+ public string Name { get; set; }
+ }
+
+ public class StringTestMapper : SimpleCRUDFluentTableMap.TableMapper
+ {
+ public StringTestMapper()
+ {
+ Map(g => g.stringkey).AsKey();
+ }
+ }
+ public class StringTest
+ {
+ public string stringkey { get; set; }
+ public string name { get; set; }
+ }
+
+ public class StrangeColumnNamesMapper : SimpleCRUDFluentTableMap.TableMapper
+ {
+ public StrangeColumnNamesMapper()
+ {
+ Map(g => g.Id).AsKey().WithColumnName("ItemId");
+ Map(g => g.StrangeWord).WithColumnName("colstringstrangeword");
+ Map(g => g.Select).WithColumnName("KeywordedProperty");
+ Map(g => g.ExtraProperty).AsNotEditable();
+ }
+ }
+ public class StrangeColumnNames
+ {
+ public int Id { get; set; }
+ public string Word { get; set; }
+ public string StrangeWord { get; set; }
+ public string Select { get; set; }
+ public string ExtraProperty { get; set; }
+ }
+
+ public class IgnoreColumnsMapper : SimpleCRUDFluentTableMap.TableMapper
+ {
+ public IgnoreColumnsMapper()
+ {
+ Map(g => g.Id).AsKey();
+ Map(g => g.IgnoreInsert).AsIgnoredInInsert();
+ Map(g => g.IgnoreUpdate).AsIgnoredInUpdate();
+ Map(g => g.IgnoreSelect).AsIgnoredInSelect();
+ Map(g => g.IgnoreAll).AsIgnoredInSelect().AsIgnoredInInsert().AsIgnoredInUpdate();
+ }
+ }
+ public class IgnoreColumns
+ {
+ public int Id { get; set; }
+ public string IgnoreInsert { get; set; }
+ public string IgnoreUpdate { get; set; }
+ public string IgnoreSelect { get; set; }
+ public string IgnoreAll { get; set; }
+ }
+
+ public class UserWithoutAutoIdentityMapper : SimpleCRUDFluentTableMap.TableMapper
+ {
+ public UserWithoutAutoIdentityMapper()
+ {
+ Map(u => u.Id).AsKey().AsRequired();
+ }
+ }
+ public class UserWithoutAutoIdentity
+ {
+ public int Id { get; set; }
+ public string Name { get; set; }
+ public int Age { get; set; }
+ }
+
+ public class KeyMasterMapper : SimpleCRUDFluentTableMap.TableMapper
+ {
+ public KeyMasterMapper()
+ {
+ Map(k => k.Key1).AsKey().AsRequired();
+ Map(k => k.Key2).AsKey().AsRequired();
+ }
+ }
+ public class KeyMaster
+ {
+ public int Key1 { get; set; }
+ public int Key2 { get; set; }
+ }
+
+ #endregion
+
+ public class Tests
+ {
+ public Tests(SimpleCRUD.Dialect dbtype)
+ {
+ _dbtype = dbtype;
+ SimpleCRUDFluentTableMap.AddAllMappingsFromAssembliesLoaded();
+ SimpleCRUDFluentTableMap.RegisterSimpleCrudFluentTableMapResolver();
+ }
+ private SimpleCRUD.Dialect _dbtype;
+
+ private IDbConnection GetOpenConnection()
+ {
+
+ IDbConnection connection;
+ if (_dbtype == SimpleCRUD.Dialect.PostgreSQL)
+ {
+ connection = new NpgsqlConnection(String.Format("Server={0};Port={1};User Id={2};Password={3};Database={4};", "localhost", "5432", "postgres", "postgrespass", "testdb"));
+ SimpleCRUD.SetDialect(SimpleCRUD.Dialect.PostgreSQL);
+ }
+ else if (_dbtype == SimpleCRUD.Dialect.SQLite)
+ {
+ connection = new SQLiteConnection("Data Source=MyDatabase.sqlite;Version=3;");
+ SimpleCRUD.SetDialect(SimpleCRUD.Dialect.SQLite);
+ }
+ else if (_dbtype == SimpleCRUD.Dialect.MySQL)
+ {
+ connection = new MySqlConnection(String.Format("Server={0};Port={1};User Id={2};Password={3};Database={4};", "localhost", "3306", "root", "admin", "testdb"));
+ SimpleCRUD.SetDialect(SimpleCRUD.Dialect.MySQL);
+ }
+ else if (_dbtype == SimpleCRUD.Dialect.DB2)
+ {
+ connection = new DB2Connection(string.Format("Server={0};UID={1};PWD={2};Database={3};", "localhost:50000", "db2admin", "db2admin", "testdb"));
+ SimpleCRUD.SetDialect(SimpleCRUD.Dialect.DB2);
+ }
+ else
+ {
+ connection = new SqlConnection($@"Data Source={Program.SQLServerName};Initial Catalog=DapperSimpleCrudTestDb;Integrated Security=True;MultipleActiveResultSets=true;");
+ SimpleCRUD.SetDialect(SimpleCRUD.Dialect.SQLServer);
+ }
+
+ connection.Open();
+ return connection;
+ }
+
+ //basic tests
+ public void TestInsertWithSpecifiedTableName()
+ {
+ using (var connection = GetOpenConnection())
+ {
+
+ var id = connection.Insert(new User { Name = "TestInsertWithSpecifiedTableName", Age = 10 });
+ var user = connection.Get(id);
+ user.Name.IsEqualTo("TestInsertWithSpecifiedTableName");
+ connection.Delete(id);
+
+ }
+ }
+
+ public void TestMassInsert()
+ {
+ //With cached strinb builder, this tests runs 2.5X faster (From 400ms to 180ms)
+ using (var connection = GetOpenConnection())
+ using (var transaction = connection.BeginTransaction())
+ {
+ for (int i = 0; i < 1000; i++)
+ {
+ var id = connection.Insert(new User { Name = $"Name #{i}", Age = i }, transaction);
+ }
+ }
+ }
+
+ public void TestMassUpdate() //356
+ {
+ //With cached strinb builder, this tests runs 2.5X faster (From 375ms to 140ms)
+ using (var connection = GetOpenConnection())
+ using (var transaction = connection.BeginTransaction())
+ {
+ var id = connection.Insert(new User { Name = "New User", Age = 0 }, transaction);
+ var user = connection.Get(id, transaction);
+
+ for (int i = 1; i <= 1000; i++)
+ {
+ user.Age = i;
+ connection.Update(user, transaction);
+ }
+ }
+ }
+
+ public void TestInsertUsingBigIntPrimaryKey()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ var id = connection.Insert(new BigCar { Make = "Big", Model = "Car" });
+ connection.Delete(id);
+
+ }
+ }
+
+ public void TestInsertUsingGenericLimitedFields()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ //arrange
+ var user = new User { Name = "User1", Age = 10, ScheduledDayOff = DayOfWeek.Friday };
+
+ //act
+ var id = connection.Insert(user);
+
+ //assert
+ var insertedUser = connection.Get(id);
+ insertedUser.ScheduledDayOff.IsNull();
+
+ connection.Delete(id);
+ }
+ }
+
+ public void TestSimpleGet()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ var id = connection.Insert(new User { Name = "UserTestSimpleGet", Age = 10 });
+ var user = connection.Get(id);
+ user.Name.IsEqualTo("UserTestSimpleGet");
+ connection.Delete(id);
+
+ }
+ }
+
+ public void TestDeleteById()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ var id = connection.Insert(new User { Name = "UserTestDeleteById", Age = 10 });
+ connection.Delete(id);
+ connection.Get(id).IsNull();
+ }
+ }
+
+ public void TestDeleteByObject()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ var id = connection.Insert(new User { Name = "TestDeleteByObject", Age = 10 });
+ var user = connection.Get(id);
+ connection.Delete(user);
+ connection.Get(id).IsNull();
+ }
+ }
+
+ public void TestSimpleGetList()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ connection.Insert(new User { Name = "TestSimpleGetList1", Age = 10 });
+ connection.Insert(new User { Name = "TestSimpleGetList2", Age = 10 });
+ var user = connection.GetList(new { });
+ user.Count().IsEqualTo(2);
+ connection.Execute("Delete from Users");
+ }
+ }
+
+ public void TestFilteredGetList()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ connection.Insert(new User { Name = "TestFilteredGetList1", Age = 10 });
+ connection.Insert(new User { Name = "TestFilteredGetList2", Age = 10 });
+ connection.Insert(new User { Name = "TestFilteredGetList3", Age = 10 });
+ connection.Insert(new User { Name = "TestFilteredGetList4", Age = 11 });
+
+ var user = connection.GetList(new { Age = 10 });
+ user.Count().IsEqualTo(3);
+ connection.Execute("Delete from Users");
+ }
+ }
+
+
+ public void TestFilteredGetListWithMultipleKeys()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ connection.Insert(new KeyMaster { Key1 = 1, Key2 = 1 });
+ connection.Insert(new KeyMaster { Key1 = 1, Key2 = 2 });
+ connection.Insert(new KeyMaster { Key1 = 1, Key2 = 3 });
+ connection.Insert(new KeyMaster { Key1 = 2, Key2 = 4 });
+
+ var keyMasters = connection.GetList(new { Key1 = 1 });
+ keyMasters.Count().IsEqualTo(3);
+ connection.Execute("Delete from KeyMaster");
+ }
+ }
+
+
+ public void TestFilteredWithSQLGetList()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ connection.Insert(new User { Name = "TestFilteredWithSQLGetList1", Age = 10 });
+ connection.Insert(new User { Name = "TestFilteredWithSQLGetList2", Age = 10 });
+ connection.Insert(new User { Name = "TestFilteredWithSQLGetList3", Age = 10 });
+ connection.Insert(new User { Name = "TestFilteredWithSQLGetList4", Age = 11 });
+
+ var user = connection.GetList("where Name like 'TestFilteredWithSQLGetList%' and Age = 10");
+ user.Count().IsEqualTo(3);
+ connection.Execute("Delete from Users");
+ }
+ }
+
+ public void TestGetListWithNullWhere()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ connection.Insert(new User { Name = "TestGetListWithNullWhere", Age = 10 });
+ var user = connection.GetList(null);
+ user.Count().IsEqualTo(1);
+ connection.Execute("Delete from Users");
+ }
+ }
+
+ public void TestGetListWithoutWhere()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ connection.Insert(new User { Name = "TestGetListWithoutWhere", Age = 10 });
+ var user = connection.GetList();
+ user.Count().IsEqualTo(1);
+ connection.Execute("Delete from Users");
+ }
+ }
+
+ public void TestsGetListWithParameters()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ connection.Insert(new User { Name = "TestsGetListWithParameters1", Age = 10 });
+ connection.Insert(new User { Name = "TestsGetListWithParameters2", Age = 10 });
+ connection.Insert(new User { Name = "TestsGetListWithParameters3", Age = 10 });
+ connection.Insert(new User { Name = "TestsGetListWithParameters4", Age = 11 });
+
+ var user = connection.GetList("where Age > @Age", new { Age = 10 });
+ user.Count().IsEqualTo(1);
+ connection.Execute("Delete from Users");
+ }
+ }
+
+ public void TestGetWithReadonlyProperty()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ var id = connection.Insert(new User { Name = "TestGetWithReadonlyProperty", Age = 10 });
+ var user = connection.Get(id);
+ user.CreatedDate.Year.IsEqualTo(DateTime.Now.Year);
+ connection.Execute("Delete from Users");
+ }
+ }
+
+ public void TestInsertWithReadonlyProperty()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ var id = connection.Insert(new User { Name = "TestInsertWithReadonlyProperty", Age = 10, CreatedDate = new DateTime(2001, 1, 1) });
+ var user = connection.Get(id);
+ //the date can't be 2001 - it should be the autogenerated date from the database
+ user.CreatedDate.Year.IsEqualTo(DateTime.Now.Year);
+ connection.Execute("Delete from Users");
+ }
+ }
+
+ public void TestUpdateWithReadonlyProperty()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ var id = connection.Insert(new User { Name = "TestUpdateWithReadonlyProperty", Age = 10 });
+ var user = connection.Get(id);
+ user.Age = 11;
+ user.CreatedDate = new DateTime(2001, 1, 1);
+ connection.Update(user);
+ user = connection.Get(id);
+ //don't allow changing created date since it has a readonly attribute
+ user.CreatedDate.Year.IsEqualTo(DateTime.Now.Year);
+ connection.Execute("Delete from Users");
+ }
+ }
+
+ public void TestGetWithNotMappedProperty()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ var id = connection.Insert(new User { Name = "TestGetWithNotMappedProperty", Age = 10, NotMappedInt = 1000 });
+ var user = connection.Get(id);
+ user.NotMappedInt.IsEqualTo(0);
+ connection.Execute("Delete from Users");
+ }
+ }
+
+ public void TestInsertWithNotMappedProperty()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ var id = connection.Insert(new User { Name = "TestInsertWithNotMappedProperty", Age = 10, CreatedDate = new DateTime(2001, 1, 1), NotMappedInt = 1000 });
+ var user = connection.Get(id);
+ user.NotMappedInt.IsEqualTo(0);
+ connection.Execute("Delete from Users");
+ }
+ }
+
+ public void TestUpdateWithNotMappedProperty()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ var id = connection.Insert(new User { Name = "TestUpdateWithNotMappedProperty", Age = 10 });
+ var user = connection.Get(id);
+ user.Age = 11;
+ user.CreatedDate = new DateTime(2001, 1, 1);
+ user.NotMappedInt = 1234;
+ connection.Update(user);
+ user = connection.Get(id);
+
+ user.NotMappedInt.IsEqualTo(0);
+
+ connection.Execute("Delete from Users");
+ }
+ }
+
+ public void TestInsertWithSpecifiedKey()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ var id = connection.Insert(new Car { Make = "Honda", Model = "Civic" });
+ id.IsEqualTo(1);
+ }
+ }
+
+ public void TestInsertWithExtraPropertiesShouldSkipNonSimpleTypesAndPropertiesMarkedEditableFalse()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ var id = connection.Insert(new Car { Make = "Honda", Model = "Civic", Users = new List { new User { Age = 12, Name = "test" } } });
+ id.IsEqualTo(2);
+ }
+ }
+
+ public void TestUpdate()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ var newid = (int)connection.Insert(new Car { Make = "Honda", Model = "Civic" });
+ var newitem = connection.Get(newid);
+ newitem.Make = "Toyota";
+ connection.Update(newitem);
+ var updateditem = connection.Get(newid);
+ updateditem.Make.IsEqualTo("Toyota");
+ connection.Delete(newid);
+ }
+ }
+
+ ///
+ /// We expect scheduled day off to NOT be updated, since it's not a property of UserEditableSettings
+ ///
+ public void TestUpdateUsingGenericLimitedFields()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ //arrange
+ var user = new User { Name = "User1", Age = 10, ScheduledDayOff = DayOfWeek.Friday };
+ user.Id = connection.Insert(user) ?? 0;
+
+ user.ScheduledDayOff = DayOfWeek.Thursday;
+ var userAsEditableSettings = (UserEditableSettings)user;
+ userAsEditableSettings.Name = "User++";
+
+ connection.Update(userAsEditableSettings);
+
+ //act
+ var insertedUser = connection.Get(user.Id);
+
+ //assert
+ insertedUser.Name.IsEqualTo("User++");
+ insertedUser.ScheduledDayOff.IsEqualTo(DayOfWeek.Friday);
+
+ connection.Delete(user.Id);
+ }
+ }
+
+ public void TestDeleteByObjectWithAttributes()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ var id = connection.Insert(new Car { Make = "Honda", Model = "Civic" });
+ var car = connection.Get(id);
+ connection.Delete(car);
+ connection.Get(id).IsNull();
+ }
+ }
+
+ public void TestDeleteByMultipleKeyObjectWithAttributes()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ var keyMaster = new KeyMaster { Key1 = 1, Key2 = 2 };
+ connection.Insert(keyMaster);
+ var car = connection.Get(new { Key1 = 1, Key2 = 2 });
+ connection.Delete(car);
+ connection.Get(keyMaster).IsNull();
+ }
+ }
+
+ public void TestComplexTypesMarkedEditableAreSaved()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ var id = (int)connection.Insert(new User { Name = "User", Age = 11, ScheduledDayOff = DayOfWeek.Friday });
+ var user1 = connection.Get(id);
+ user1.ScheduledDayOff.IsEqualTo(DayOfWeek.Friday);
+ connection.Delete(user1);
+ }
+ }
+
+ public void TestNullableSimpleTypesAreSaved()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ var id = (int)connection.Insert(new User1 { Name = "User", Age = 11, ScheduledDayOff = 2 });
+ var user1 = connection.Get(id);
+ user1.ScheduledDayOff.IsEqualTo(2);
+ connection.Delete(user1);
+ }
+ }
+
+ public void TestInsertIntoDifferentSchema()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ var id = connection.Insert(new CarLog { LogNotes = "blah blah blah" });
+ id.IsEqualTo(1);
+ connection.Delete(id);
+
+ }
+ }
+
+ public void TestGetFromDifferentSchema()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ var id = connection.Insert(new CarLog { LogNotes = "TestGetFromDifferentSchema" });
+ var carlog = connection.Get(id);
+ carlog.LogNotes.IsEqualTo("TestGetFromDifferentSchema");
+ connection.Delete(id);
+ }
+ }
+
+ public void TestTryingToGetFromTableInSchemaWithoutDataAnnotationShouldFail()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ try
+ {
+ connection.Get(1);
+ }
+ catch (Exception)
+ {
+ //we expect to get an exception, so return
+ return;
+ }
+
+ //if we get here without throwing an exception, the test failed.
+ throw new ApplicationException("Expected exception");
+ }
+ }
+
+ public void TestGetFromTableWithNonIntPrimaryKey()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ //note - there's not support for inserts without a non-int id, so drop down to a normal execute
+ connection.Execute("INSERT INTO CITY (NAME, POPULATION) VALUES ('Morgantown', 31000)");
+ var city = connection.Get("Morgantown");
+ city.Population.IsEqualTo(31000);
+ }
+ }
+
+ public void TestDeleteFromTableWithNonIntPrimaryKey()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ //note - there's not support for inserts without a non-int id, so drop down to a normal execute
+ connection.Execute("INSERT INTO CITY (NAME, POPULATION) VALUES ('Fairmont', 18737)");
+ connection.Delete("Fairmont").IsEqualTo(1);
+ }
+ }
+
+ public void TestNullableEnumInsert()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ connection.Insert(new User { Name = "Enum-y", Age = 10, ScheduledDayOff = DayOfWeek.Thursday });
+ var user = connection.GetList(new { Name = "Enum-y" }).FirstOrDefault() ?? new User();
+ user.ScheduledDayOff.IsEqualTo(DayOfWeek.Thursday);
+ connection.Delete(user.Id);
+ }
+ }
+
+ //dialect test
+
+ public void TestChangeDialect()
+ {
+ SimpleCRUD.SetDialect(SimpleCRUD.Dialect.SQLServer);
+ SimpleCRUD.GetDialect().IsEqualTo(SimpleCRUD.Dialect.SQLServer.ToString());
+ SimpleCRUD.SetDialect(SimpleCRUD.Dialect.PostgreSQL);
+ SimpleCRUD.GetDialect().IsEqualTo(SimpleCRUD.Dialect.PostgreSQL.ToString());
+ }
+
+
+ // A GUID is being created and returned on insert but never actually
+ //applied to the insert query.
+
+ //This can be seen on a table where the key
+ //is a GUID and defaults to (newid()) and no GUID is provided on the
+ //insert. Dapper will generate a GUID but it is not applied so the GUID is
+ //generated by newid() but the Dapper GUID is returned instead which is
+ //incorrect.
+
+
+ //GUID primary key tests
+
+ public void TestInsertIntoTableWithUnspecifiedGuidKey()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ var id = connection.Insert(new GUIDTest { Name = "GuidUser" });
+ id.GetType().Name.IsEqualTo("Guid");
+ var record = connection.Get(id);
+ record.Name.IsEqualTo("GuidUser");
+ connection.Delete(id);
+ }
+ }
+
+ public void TestInsertIntoTableWithGuidKey()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ var guid = new Guid("1a6fb33d-7141-47a0-b9fa-86a1a1945da9");
+ var id = connection.Insert(new GUIDTest { Name = "InsertIntoTableWithGuidKey", Id = guid });
+ id.IsEqualTo(guid);
+ connection.Delete(id);
+ }
+ }
+
+ public void TestGetRecordWithGuidKey()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ var guid = new Guid("2a6fb33d-7141-47a0-b9fa-86a1a1945da9");
+ connection.Insert(new GUIDTest { Name = "GetRecordWithGuidKey", Id = guid });
+ var id = connection.GetList().First().Id;
+ var record = connection.Get(id);
+ record.Name.IsEqualTo("GetRecordWithGuidKey");
+ connection.Delete(id);
+
+ }
+ }
+
+ public void TestDeleteRecordWithGuidKey()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ var guid = new Guid("3a6fb33d-7141-47a0-b9fa-86a1a1945da9");
+ connection.Insert(new GUIDTest { Name = "DeleteRecordWithGuidKey", Id = guid });
+ var id = connection.GetList().First().Id;
+ connection.Delete(id);
+ connection.Get(id).IsNull();
+ }
+ }
+ public void TestInsertIntoTableWithStringKey()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ var id = connection.Insert(new StringTest { stringkey = "123xyz", name = "Bob" });
+ id.IsEqualTo("123xyz");
+ connection.Delete(id);
+ }
+ }
+
+#if !NET40
+ //async tests
+ public void TestMultiInsertASync()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ connection.InsertAsync(new User { Name = "TestMultiInsertASync1", Age = 10 });
+ connection.InsertAsync(new User { Name = "TestMultiInsertASync2", Age = 10 });
+ connection.InsertAsync(new User { Name = "TestMultiInsertASync3", Age = 10 });
+ connection.InsertAsync(new User { Name = "TestMultiInsertASync4", Age = 11 });
+ System.Threading.Thread.Sleep(300);
+ //tiny wait to let the inserts happen
+ var list = connection.GetList(new { Age = 10 });
+ list.Count().IsEqualTo(3);
+ connection.Execute("Delete from Users");
+
+ }
+ }
+
+ public async void MultiInsertWithGuidAsync()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ await connection.InsertAsync(new GUIDTest { Name = "MultiInsertWithGuidAsync" });
+ await connection.InsertAsync(new GUIDTest { Name = "MultiInsertWithGuidAsync" });
+ await connection.InsertAsync(new GUIDTest { Name = "MultiInsertWithGuidAsync" });
+ await connection.InsertAsync(new GUIDTest { Name = "MultiInsertWithGuidAsync" });
+ //tiny wait to let the inserts happen
+ System.Threading.Thread.Sleep(300);
+ var list = connection.GetList(new { Name = "MultiInsertWithGuidAsync" });
+ list.Count().IsEqualTo(4);
+ connection.Execute("Delete from GUIDTest");
+ }
+ }
+
+ public void TestSimpleGetAsync()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ var id = connection.Insert(new User { Name = "TestSimpleGetAsync", Age = 10 });
+ var user = connection.GetAsync(id);
+ user.Result.Name.IsEqualTo("TestSimpleGetAsync");
+ connection.Delete(id);
+ }
+ }
+
+ public void TestMultipleKeyGetAsync()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ var keyMaster = new KeyMaster { Key1 = 1, Key2 = 2 };
+ connection.Insert(keyMaster);
+ var result = connection.GetAsync(new { Key1 = 1, Key2 = 2 });
+ result.Result.Key1.IsEqualTo(1);
+ result.Result.Key2.IsEqualTo(2);
+ connection.Delete(keyMaster);
+ }
+ }
+
+ public async void TestDeleteByIdAsync()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ var id = connection.Insert(new User { Name = "UserAsyncDelete", Age = 10 });
+ await connection.DeleteAsync(id);
+ //tiny wait to let the delete happen
+ connection.Get(id).IsNull();
+ }
+ }
+
+ public void TestDeleteByObjectAsync()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ var id = connection.Insert(new User { Name = "TestDeleteByObjectAsync", Age = 10 });
+ var user = connection.Get(id);
+ connection.DeleteAsync(user);
+ connection.Get(id).IsNull();
+ connection.Delete(id);
+ }
+ }
+
+ public void TestDeleteByMultipleKeyObject()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ var keyMaster = new KeyMaster { Key1 = 1, Key2 = 2 };
+ connection.Insert(keyMaster);
+ connection.Get(keyMaster);
+ connection.Delete(new { Key1 = 1, Key2 = 2 });
+ connection.Get(keyMaster).IsNull();
+ connection.Delete(keyMaster);
+ }
+ }
+
+ public void TestSimpleGetListAsync()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ connection.Insert(new User { Name = "TestSimpleGetListAsync1", Age = 10 });
+ connection.Insert(new User { Name = "TestSimpleGetListAsync2", Age = 10 });
+ var user = connection.GetListAsync(new { });
+ user.Result.Count().IsEqualTo(2);
+ connection.Execute("Delete from Users");
+ }
+ }
+
+ public void TestFilteredGetListAsync()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ connection.Insert(new User { Name = "TestFilteredGetListAsync1", Age = 10 });
+ connection.Insert(new User { Name = "TestFilteredGetListAsync2", Age = 10 });
+ connection.Insert(new User { Name = "TestFilteredGetListAsync3", Age = 10 });
+ connection.Insert(new User { Name = "TestFilteredGetListAsync4", Age = 11 });
+
+ var user = connection.GetListAsync(new { Age = 10 });
+ user.Result.Count().IsEqualTo(3);
+ connection.Execute("Delete from Users");
+ }
+ }
+
+ public void TestFilteredGetListParametersAsync()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ connection.Insert(new User { Name = "TestFilteredGetListParametersAsync1", Age = 10 });
+ connection.Insert(new User { Name = "TestFilteredGetListParametersAsync2", Age = 10 });
+ connection.Insert(new User { Name = "TestFilteredGetListParametersAsync3", Age = 10 });
+ connection.Insert(new User { Name = "TestFilteredGetListParametersAsync4", Age = 11 });
+
+ var user = connection.GetListAsync("where Age = @Age", new { Age = 10 });
+ user.Result.Count().IsEqualTo(3);
+ connection.Execute("Delete from Users");
+ }
+ }
+
+ public void TestRecordCountAsync()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ int x = 0;
+ do
+ {
+ connection.Insert(new User { Name = "Person " + x, Age = x, CreatedDate = DateTime.Now, ScheduledDayOff = DayOfWeek.Thursday });
+ x++;
+ } while (x < 30);
+
+ var resultlist = connection.GetList();
+ resultlist.Count().IsEqualTo(30);
+ connection.RecordCountAsync().Result.IsEqualTo(30);
+
+ connection.RecordCountAsync("where age = 10 or age = 11").Result.IsEqualTo(2);
+
+
+ connection.Execute("Delete from Users");
+ }
+
+ }
+
+ public void TestRecordCountByObjectAsync()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ int x = 0;
+ do
+ {
+ connection.Insert(new User { Name = "Person " + x, Age = x, CreatedDate = DateTime.Now, ScheduledDayOff = DayOfWeek.Thursday });
+ x++;
+ } while (x < 30);
+
+ var resultlist = connection.GetList();
+ resultlist.Count().IsEqualTo(30);
+ connection.RecordCountAsync().Result.IsEqualTo(30);
+
+ connection.RecordCountAsync(new { age = 10 }).Result.IsEqualTo(1);
+
+
+ connection.Execute("Delete from Users");
+ }
+
+ }
+
+ public void TestInsertWithSpecifiedPrimaryKeyAsync()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ var id = connection.InsertAsync(new UserWithoutAutoIdentity() { Id = 999, Name = "User999Async", Age = 10 });
+ id.Result.IsEqualTo(999);
+ var user = connection.GetAsync(999);
+ user.Result.Name.IsEqualTo("User999Async");
+ connection.Execute("Delete from UserWithoutAutoIdentity");
+ }
+ }
+
+
+ public async void TestInsertWithMultiplePrimaryKeysAsync()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ var keyMaster = new KeyMaster { Key1 = 1, Key2 = 2 };
+ await connection.InsertAsync(keyMaster);
+ var result = connection.GetAsync(new { Key1 = 1, Key2 = 2 });
+ result.Result.Key1.IsEqualTo(1);
+ result.Result.Key2.IsEqualTo(2);
+ connection.Execute("Delete from KeyMaster");
+ }
+ }
+ public void TestInsertUsingGenericLimitedFieldsAsync()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ //arrange
+ var user = new User { Name = "User1", Age = 10, ScheduledDayOff = DayOfWeek.Friday };
+
+ //act
+ var idTask = connection.InsertAsync(user);
+ idTask.Wait();
+ var id = idTask.Result;
+
+ //assert
+ var insertedUser = connection.Get(id);
+ insertedUser.ScheduledDayOff.IsNull();
+
+ connection.Delete(id);
+ }
+ }
+
+ ///
+ /// We expect scheduled day off to NOT be updated, since it's not a property of UserEditableSettings
+ ///
+ public void TestUpdateUsingGenericLimitedFieldsAsync()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ //arrange
+ var user = new User { Name = "User1", Age = 10, ScheduledDayOff = DayOfWeek.Friday };
+ user.Id = connection.Insert(user) ?? 0;
+
+ user.ScheduledDayOff = DayOfWeek.Thursday;
+ var userAsEditableSettings = (UserEditableSettings)user;
+ userAsEditableSettings.Name = "User++";
+
+ connection.UpdateAsync(userAsEditableSettings).Wait();
+
+ //act
+ var insertedUser = connection.Get(user.Id);
+
+ //assert
+ insertedUser.Name.IsEqualTo("User++");
+ insertedUser.ScheduledDayOff.IsEqualTo(DayOfWeek.Friday);
+
+ connection.Delete(user.Id);
+ }
+ }
+#endif
+ //column attribute tests
+
+ public void TestInsertWithSpecifiedColumnName()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ var itemId = connection.Insert(new StrangeColumnNames { Word = "InsertWithSpecifiedColumnName", StrangeWord = "Strange 1" });
+ itemId.IsEqualTo(1);
+ connection.Delete(itemId);
+
+ }
+ }
+
+ public void TestDeleteByObjectWithSpecifiedColumnName()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ var itemId = connection.Insert(new StrangeColumnNames { Word = "TestDeleteByObjectWithSpecifiedColumnName", StrangeWord = "Strange 1" });
+ var strange = connection.Get(itemId);
+ connection.Delete(strange);
+ connection.Get(itemId).IsNull();
+ }
+ }
+
+ public void TestSimpleGetListWithSpecifiedColumnName()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ var id1 = connection.Insert(new StrangeColumnNames { Word = "TestSimpleGetListWithSpecifiedColumnName1", StrangeWord = "Strange 2", });
+ var id2 = connection.Insert(new StrangeColumnNames { Word = "TestSimpleGetListWithSpecifiedColumnName2", StrangeWord = "Strange 3", });
+ var strange = connection.GetList(new { });
+ strange.First().StrangeWord.IsEqualTo("Strange 2");
+ strange.Count().IsEqualTo(2);
+ connection.Delete(id1);
+ connection.Delete(id2);
+ }
+ }
+
+ public void TestUpdateWithSpecifiedColumnName()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ var newid = (int)connection.Insert(new StrangeColumnNames { Word = "Word Insert", StrangeWord = "Strange Insert" });
+ var newitem = connection.Get(newid);
+ newitem.Word = "Word Update";
+ connection.Update(newitem);
+ var updateditem = connection.Get(newid);
+ updateditem.Word.IsEqualTo("Word Update");
+ connection.Delete(newid);
+ }
+ }
+
+ public void TestFilteredGetListWithSpecifiedColumnName()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ connection.Insert(new StrangeColumnNames { Word = "Word 5", StrangeWord = "Strange 1", });
+ connection.Insert(new StrangeColumnNames { Word = "Word 6", StrangeWord = "Strange 2", });
+ connection.Insert(new StrangeColumnNames { Word = "Word 7", StrangeWord = "Strange 2", });
+ connection.Insert(new StrangeColumnNames { Word = "Word 8", StrangeWord = "Strange 2", });
+
+ var strange = connection.GetList(new { StrangeWord = "Strange 2" });
+ strange.Count().IsEqualTo(3);
+ connection.Execute("Delete from StrangeColumnNames");
+ }
+ }
+
+ public void TestGetListPaged()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ int x = 0;
+ do
+ {
+ connection.Insert(new User { Name = "Person " + x, Age = x, CreatedDate = DateTime.Now, ScheduledDayOff = DayOfWeek.Thursday });
+ x++;
+ } while (x < 30);
+
+ var resultlist = connection.GetListPaged(2, 10, null, null);
+ resultlist.Count().IsEqualTo(10);
+ resultlist.Skip(4).First().Name.IsEqualTo("Person 14");
+ connection.Execute("Delete from Users");
+ }
+ }
+
+ public void TestGetListPagedWithParameters()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ int x = 0;
+ do
+ {
+ connection.Insert(new User { Name = "Person " + x, Age = x, CreatedDate = DateTime.Now, ScheduledDayOff = DayOfWeek.Thursday });
+ x++;
+ } while (x < 30);
+
+ var resultlist = connection.GetListPaged(1, 30, "where Age > @Age", null, new { Age = 14 });
+ resultlist.Count().IsEqualTo(15);
+ resultlist.First().Name.IsEqualTo("Person 15");
+ connection.Execute("Delete from Users");
+ }
+ }
+
+
+ public void TestGetListPagedWithSpecifiedPrimaryKey()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ int x = 0;
+ do
+ {
+ connection.Insert(new StrangeColumnNames { Word = "Word " + x, StrangeWord = "Strange " + x });
+ x++;
+ } while (x < 30);
+
+ var resultlist = connection.GetListPaged(2, 10, null, null);
+ resultlist.Count().IsEqualTo(10);
+ resultlist.Skip(4).First().Word.IsEqualTo("Word 14");
+ connection.Execute("Delete from StrangeColumnNames");
+ }
+ }
+ public void TestGetListPagedWithWhereClause()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ int x = 0;
+ do
+ {
+ connection.Insert(new User { Name = "Person " + x, Age = x, CreatedDate = DateTime.Now, ScheduledDayOff = DayOfWeek.Thursday });
+ x++;
+ } while (x < 30);
+
+ var resultlist1 = connection.GetListPaged(1, 3, "Where Name LIKE 'Person 2%'", "age desc");
+ resultlist1.Count().IsEqualTo(3);
+
+ var resultlist = connection.GetListPaged(2, 3, "Where Name LIKE 'Person 2%'", "age desc");
+ resultlist.Count().IsEqualTo(3);
+ resultlist.Skip(1).First().Name.IsEqualTo("Person 25");
+
+ connection.Execute("Delete from Users");
+ }
+ }
+
+ public void TestDeleteListWithWhereClause()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ int x = 0;
+ do
+ {
+ connection.Insert(new User { Name = "Person " + x, Age = x, CreatedDate = DateTime.Now, ScheduledDayOff = DayOfWeek.Thursday });
+ x++;
+ } while (x < 30);
+
+ connection.DeleteList("Where age > 9");
+ var resultlist = connection.GetList();
+ resultlist.Count().IsEqualTo(10);
+ connection.Execute("Delete from Users");
+ }
+ }
+
+ public void TestDeleteListWithWhereObject()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ int x = 0;
+ do
+ {
+ connection.Insert(new User { Name = "Person " + x, Age = x, CreatedDate = DateTime.Now, ScheduledDayOff = DayOfWeek.Thursday });
+ x++;
+ } while (x < 10);
+
+ connection.DeleteList(new { age = 9 });
+ var resultlist = connection.GetList();
+ resultlist.Count().IsEqualTo(9);
+ connection.Execute("Delete from Users");
+ }
+ }
+
+ public void TestDeleteListWithParameters()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ int x = 1;
+ do
+ {
+ connection.Insert(new User { Name = "Person " + x, Age = x, CreatedDate = DateTime.Now, ScheduledDayOff = DayOfWeek.Thursday });
+ x++;
+ } while (x < 10);
+
+ connection.DeleteList("where age >= @Age", new { Age = 9 });
+ var resultlist = connection.GetList();
+ resultlist.Count().IsEqualTo(8);
+ connection.Execute("Delete from Users");
+ }
+ }
+
+ public void TestRecordCountWhereClause()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ int x = 0;
+ do
+ {
+ connection.Insert(new User { Name = "Person " + x, Age = x, CreatedDate = DateTime.Now, ScheduledDayOff = DayOfWeek.Thursday });
+ x++;
+ } while (x < 30);
+
+ var resultlist = connection.GetList();
+ resultlist.Count().IsEqualTo(30);
+ connection.RecordCount().IsEqualTo(30);
+
+ connection.RecordCount("where age = 10 or age = 11").IsEqualTo(2);
+
+
+ connection.Execute("Delete from Users");
+ }
+
+ }
+
+ public void TestRecordCountWhereObject()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ int x = 0;
+ do
+ {
+ connection.Insert(new User { Name = "Person " + x, Age = x, CreatedDate = DateTime.Now, ScheduledDayOff = DayOfWeek.Thursday });
+ x++;
+ } while (x < 30);
+
+ var resultlist = connection.GetList();
+ resultlist.Count().IsEqualTo(30);
+ connection.RecordCount().IsEqualTo(30);
+
+ connection.RecordCount(new { age = 10 }).IsEqualTo(1);
+
+
+ connection.Execute("Delete from Users");
+ }
+
+ }
+
+ public void TestRecordCountParameters()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ int x = 0;
+ do
+ {
+ connection.Insert(new User { Name = "Person " + x, Age = x, CreatedDate = DateTime.Now, ScheduledDayOff = DayOfWeek.Thursday });
+ x++;
+ } while (x < 30);
+
+ var resultlist = connection.GetList();
+ resultlist.Count().IsEqualTo(30);
+ connection.RecordCount("where Age > 15").IsEqualTo(14);
+
+
+ connection.Execute("Delete from Users");
+ }
+
+ }
+
+ public void TestInsertWithSpecifiedPrimaryKey()
+ {
+ using (var connection = GetOpenConnection())
+ {
+ var id = connection.Insert(new UserWithoutAutoIdentity() { Id = 999, Name = "User999", Age = 10 });
+ id.IsEqualTo(999);
+ var user = connection.Get