mirror of
https://github.com/DapperLib/Dapper.git
synced 2026-10-29 13:19:20 +08:00
Merge branch 'AsyncCrud' of https://github.com/AdamAtDotNet/dapper-dot-net into AdamAtDotNet-AsyncCrud
This commit is contained in:
+12
-1
@@ -1,7 +1,7 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 2013
|
||||
VisualStudioVersion = 12.0.30723.0
|
||||
VisualStudioVersion = 12.0.30324.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DapperTests NET40", "Tests\DapperTests NET40.csproj", "{A2A80512-11F4-4028-A995-505463632C84}"
|
||||
EndProject
|
||||
@@ -44,6 +44,7 @@ EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapper.Contrib NET45", "Dapper.Contrib NET45\Dapper.Contrib NET45.csproj", "{302EC82F-A81B-48C5-B653-B5C75D2BD103}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapper.Rainbow NET45", "Dapper.Rainbow NET45\Dapper.Rainbow NET45.csproj", "{DB42428F-3C2B-4C9E-9B7A-5E43B53D6613}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapper.Contrib.Tests NET45", "Dapper.Contrib.Tests NET45\Dapper.Contrib.Tests NET45.csproj", "{7A85178F-4ADC-4E4C-BF08-17FC99488A9A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@@ -195,6 +196,16 @@ Global
|
||||
{DB42428F-3C2B-4C9E-9B7A-5E43B53D6613}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{DB42428F-3C2B-4C9E-9B7A-5E43B53D6613}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{DB42428F-3C2B-4C9E-9B7A-5E43B53D6613}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{7A85178F-4ADC-4E4C-BF08-17FC99488A9A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7A85178F-4ADC-4E4C-BF08-17FC99488A9A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7A85178F-4ADC-4E4C-BF08-17FC99488A9A}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{7A85178F-4ADC-4E4C-BF08-17FC99488A9A}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{7A85178F-4ADC-4E4C-BF08-17FC99488A9A}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{7A85178F-4ADC-4E4C-BF08-17FC99488A9A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7A85178F-4ADC-4E4C-BF08-17FC99488A9A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7A85178F-4ADC-4E4C-BF08-17FC99488A9A}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{7A85178F-4ADC-4E4C-BF08-17FC99488A9A}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{7A85178F-4ADC-4E4C-BF08-17FC99488A9A}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -44,9 +44,7 @@
|
||||
<Compile Include="..\Dapper.Contrib\Properties\AssemblyInfo.cs">
|
||||
<Link>AssemblyInfo.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Dapper.Contrib\SqlMapperExtensions.cs">
|
||||
<Link>SqlMapperExtensions.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="SqlMapperExtensions.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Properties\" />
|
||||
|
||||
@@ -0,0 +1,854 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Reflection.Emit;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Dapper;
|
||||
|
||||
#pragma warning disable 1573, 1591 // xml comments
|
||||
|
||||
namespace Dapper.Contrib.Extensions
|
||||
{
|
||||
|
||||
public static class SqlMapperExtensions
|
||||
{
|
||||
public interface IProxy
|
||||
{
|
||||
bool IsDirty { get; set; }
|
||||
}
|
||||
|
||||
private static readonly ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>> KeyProperties = new ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>>();
|
||||
private static readonly ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>> TypeProperties = new ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>>();
|
||||
private static readonly ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>> ComputedProperties = new ConcurrentDictionary<RuntimeTypeHandle, IEnumerable<PropertyInfo>>();
|
||||
private static readonly ConcurrentDictionary<RuntimeTypeHandle, string> GetQueries = new ConcurrentDictionary<RuntimeTypeHandle, string>();
|
||||
private static readonly ConcurrentDictionary<RuntimeTypeHandle, string> TypeTableName = new ConcurrentDictionary<RuntimeTypeHandle, string>();
|
||||
|
||||
private static readonly Dictionary<string, ISqlAdapter> AdapterDictionary = new Dictionary<string, ISqlAdapter>() {
|
||||
{"sqlconnection", new SqlServerAdapter()},
|
||||
{"npgsqlconnection", new PostgresAdapter()},
|
||||
{"sqliteconnection", new SQLiteAdapter()}
|
||||
};
|
||||
private static IEnumerable<PropertyInfo> ComputedPropertiesCache(Type type)
|
||||
{
|
||||
IEnumerable<PropertyInfo> pi;
|
||||
if (ComputedProperties.TryGetValue(type.TypeHandle, out pi))
|
||||
{
|
||||
return pi;
|
||||
}
|
||||
|
||||
var computedProperties = TypePropertiesCache(type).Where(p => p.GetCustomAttributes(true).Any(a => a is ComputedAttribute)).ToList();
|
||||
|
||||
ComputedProperties[type.TypeHandle] = computedProperties;
|
||||
return computedProperties;
|
||||
}
|
||||
private static IEnumerable<PropertyInfo> KeyPropertiesCache(Type type)
|
||||
{
|
||||
|
||||
IEnumerable<PropertyInfo> pi;
|
||||
if (KeyProperties.TryGetValue(type.TypeHandle,out pi))
|
||||
{
|
||||
return pi;
|
||||
}
|
||||
|
||||
var allProperties = TypePropertiesCache(type);
|
||||
var keyProperties = allProperties.Where(p => p.GetCustomAttributes(true).Any(a => a is KeyAttribute)).ToList();
|
||||
|
||||
if (keyProperties.Count == 0)
|
||||
{
|
||||
var idProp = allProperties.Where(p => p.Name.ToLower() == "id").FirstOrDefault();
|
||||
if (idProp != null)
|
||||
{
|
||||
keyProperties.Add(idProp);
|
||||
}
|
||||
}
|
||||
|
||||
KeyProperties[type.TypeHandle] = keyProperties;
|
||||
return keyProperties;
|
||||
}
|
||||
private static IEnumerable<PropertyInfo> TypePropertiesCache(Type type)
|
||||
{
|
||||
IEnumerable<PropertyInfo> pis;
|
||||
if (TypeProperties.TryGetValue(type.TypeHandle, out pis))
|
||||
{
|
||||
return pis;
|
||||
}
|
||||
|
||||
var properties = type.GetProperties().Where(IsWriteable).ToArray();
|
||||
TypeProperties[type.TypeHandle] = properties;
|
||||
return properties;
|
||||
}
|
||||
|
||||
public static bool IsWriteable(PropertyInfo pi)
|
||||
{
|
||||
object[] attributes = pi.GetCustomAttributes(typeof (WriteAttribute), false);
|
||||
if (attributes.Length == 1)
|
||||
{
|
||||
WriteAttribute write = (WriteAttribute) attributes[0];
|
||||
return write.Write;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a single entity by a single id from table "Ts". T must be of interface type.
|
||||
/// Id must be marked with [Key] attribute.
|
||||
/// Created entity is tracked/intercepted for changes and used by the Update() extension.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Interface type to create and populate</typeparam>
|
||||
/// <param name="connection">Open SqlConnection</param>
|
||||
/// <param name="id">Id of the entity to get, must be marked with [Key] attribute</param>
|
||||
/// <returns>Entity of T</returns>
|
||||
public static T Get<T>(this IDbConnection connection, dynamic id, IDbTransaction transaction = null, int? commandTimeout = null) where T : class
|
||||
{
|
||||
var type = typeof(T);
|
||||
string sql;
|
||||
if (!GetQueries.TryGetValue(type.TypeHandle, out sql))
|
||||
{
|
||||
var keys = KeyPropertiesCache(type);
|
||||
if (keys.Count() > 1)
|
||||
throw new DataException("Get<T> only supports an entity with a single [Key] property");
|
||||
if (keys.Count() == 0)
|
||||
throw new DataException("Get<T> only supports en entity with a [Key] property");
|
||||
|
||||
var onlyKey = keys.First();
|
||||
|
||||
var name = GetTableName(type);
|
||||
|
||||
// TODO: pluralizer
|
||||
// TODO: query information schema and only select fields that are both in information schema and underlying class / interface
|
||||
sql = "select * from " + name + " where " + onlyKey.Name + " = @id";
|
||||
GetQueries[type.TypeHandle] = sql;
|
||||
}
|
||||
|
||||
var dynParms = new DynamicParameters();
|
||||
dynParms.Add("@id", id);
|
||||
|
||||
T obj = null;
|
||||
|
||||
if (type.IsInterface)
|
||||
{
|
||||
var res = connection.Query(sql, dynParms).FirstOrDefault() as IDictionary<string, object>;
|
||||
|
||||
if (res == null)
|
||||
return (T)((object)null);
|
||||
|
||||
obj = ProxyGenerator.GetInterfaceProxy<T>();
|
||||
|
||||
foreach (var property in TypePropertiesCache(type))
|
||||
{
|
||||
var val = res[property.Name];
|
||||
property.SetValue(obj, val, null);
|
||||
}
|
||||
|
||||
((IProxy)obj).IsDirty = false; //reset change tracking and return
|
||||
}
|
||||
else
|
||||
{
|
||||
obj = connection.Query<T>(sql, dynParms, transaction: transaction, commandTimeout: commandTimeout).FirstOrDefault();
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a single entity by a single id from table "Ts" asynchronously using .NET 4.5 Task. T must be of interface type.
|
||||
/// Id must be marked with [Key] attribute.
|
||||
/// Created entity is tracked/intercepted for changes and used by the Update() extension.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Interface type to create and populate</typeparam>
|
||||
/// <param name="connection">Open SqlConnection</param>
|
||||
/// <param name="id">Id of the entity to get, must be marked with [Key] attribute</param>
|
||||
/// <returns>Entity of T</returns>
|
||||
public static async Task<T> GetAsync<T>(this IDbConnection connection, dynamic id, IDbTransaction transaction = null, int? commandTimeout = null) where T : class
|
||||
{
|
||||
var type = typeof(T);
|
||||
string sql;
|
||||
if (!GetQueries.TryGetValue(type.TypeHandle, out sql))
|
||||
{
|
||||
var keys = KeyPropertiesCache(type);
|
||||
if (keys.Count() > 1)
|
||||
throw new DataException("Get<T> only supports an entity with a single [Key] property");
|
||||
if (keys.Count() == 0)
|
||||
throw new DataException("Get<T> only supports en entity with a [Key] property");
|
||||
|
||||
var onlyKey = keys.First();
|
||||
|
||||
var name = GetTableName(type);
|
||||
|
||||
// TODO: pluralizer
|
||||
// TODO: query information schema and only select fields that are both in information schema and underlying class / interface
|
||||
sql = "select * from " + name + " where " + onlyKey.Name + " = @id";
|
||||
GetQueries[type.TypeHandle] = sql;
|
||||
}
|
||||
|
||||
var dynParms = new DynamicParameters();
|
||||
dynParms.Add("@id", id);
|
||||
|
||||
T obj = null;
|
||||
|
||||
if (type.IsInterface)
|
||||
{
|
||||
var res = (await connection.QueryAsync<dynamic>(sql, dynParms)).FirstOrDefault() as IDictionary<string, object>;
|
||||
|
||||
if (res == null)
|
||||
return (T)((object)null);
|
||||
|
||||
obj = ProxyGenerator.GetInterfaceProxy<T>();
|
||||
|
||||
foreach (var property in TypePropertiesCache(type))
|
||||
{
|
||||
var val = res[property.Name];
|
||||
property.SetValue(obj, val, null);
|
||||
}
|
||||
|
||||
((IProxy)obj).IsDirty = false; //reset change tracking and return
|
||||
}
|
||||
else
|
||||
{
|
||||
obj = (await connection.QueryAsync<T>(sql, dynParms, transaction: transaction, commandTimeout: commandTimeout)).FirstOrDefault();
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
private static string GetTableName(Type type)
|
||||
{
|
||||
string name;
|
||||
if (!TypeTableName.TryGetValue(type.TypeHandle, out name))
|
||||
{
|
||||
name = type.Name + "s";
|
||||
if (type.IsInterface && name.StartsWith("I"))
|
||||
name = name.Substring(1);
|
||||
|
||||
//NOTE: This as dynamic trick should be able to handle both our own Table-attribute as well as the one in EntityFramework
|
||||
var tableattr = type.GetCustomAttributes(false).Where(attr => attr.GetType().Name == "TableAttribute").SingleOrDefault() as
|
||||
dynamic;
|
||||
if (tableattr != null)
|
||||
name = tableattr.Name;
|
||||
TypeTableName[type.TypeHandle] = name;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts an entity into table "Ts" and returns identity id.
|
||||
/// </summary>
|
||||
/// <param name="connection">Open SqlConnection</param>
|
||||
/// <param name="entityToInsert">Entity to insert</param>
|
||||
/// <returns>Identity of inserted entity</returns>
|
||||
public static long Insert<T>(this IDbConnection connection, T entityToInsert, IDbTransaction transaction = null, int? commandTimeout = null) where T : class
|
||||
{
|
||||
|
||||
var type = typeof(T);
|
||||
|
||||
var name = GetTableName(type);
|
||||
|
||||
var sbColumnList = new StringBuilder(null);
|
||||
|
||||
var allProperties = TypePropertiesCache(type);
|
||||
var keyProperties = KeyPropertiesCache(type);
|
||||
var computedProperties = ComputedPropertiesCache(type);
|
||||
var allPropertiesExceptKeyAndComputed = allProperties.Except(keyProperties.Union(computedProperties));
|
||||
|
||||
for (var i = 0; i < allPropertiesExceptKeyAndComputed.Count(); i++)
|
||||
{
|
||||
var property = allPropertiesExceptKeyAndComputed.ElementAt(i);
|
||||
sbColumnList.AppendFormat("[{0}]", property.Name);
|
||||
if (i < allPropertiesExceptKeyAndComputed.Count() - 1)
|
||||
sbColumnList.Append(", ");
|
||||
}
|
||||
|
||||
var sbParameterList = new StringBuilder(null);
|
||||
for (var i = 0; i < allPropertiesExceptKeyAndComputed.Count(); i++)
|
||||
{
|
||||
var property = allPropertiesExceptKeyAndComputed.ElementAt(i);
|
||||
sbParameterList.AppendFormat("@{0}", property.Name);
|
||||
if (i < allPropertiesExceptKeyAndComputed.Count() - 1)
|
||||
sbParameterList.Append(", ");
|
||||
}
|
||||
ISqlAdapter adapter = GetFormatter(connection);
|
||||
int id = adapter.Insert(connection, transaction, commandTimeout, name, sbColumnList.ToString(), sbParameterList.ToString(), keyProperties, entityToInsert);
|
||||
return id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts an entity into table "Ts" asynchronously using .NET 4.5 Task and returns identity id.
|
||||
/// </summary>
|
||||
/// <param name="connection">Open SqlConnection</param>
|
||||
/// <param name="entityToInsert">Entity to insert</param>
|
||||
/// <returns>Identity of inserted entity</returns>
|
||||
public static async Task<long> InsertAsync<T>(this IDbConnection connection, T entityToInsert, IDbTransaction transaction = null, int? commandTimeout = null) where T : class
|
||||
{
|
||||
|
||||
var type = typeof(T);
|
||||
|
||||
var name = GetTableName(type);
|
||||
|
||||
var sbColumnList = new StringBuilder(null);
|
||||
|
||||
var allProperties = TypePropertiesCache(type);
|
||||
var keyProperties = KeyPropertiesCache(type);
|
||||
var computedProperties = ComputedPropertiesCache(type);
|
||||
var allPropertiesExceptKeyAndComputed = allProperties.Except(keyProperties.Union(computedProperties));
|
||||
|
||||
for (var i = 0; i < allPropertiesExceptKeyAndComputed.Count(); i++)
|
||||
{
|
||||
var property = allPropertiesExceptKeyAndComputed.ElementAt(i);
|
||||
sbColumnList.AppendFormat("[{0}]", property.Name);
|
||||
if (i < allPropertiesExceptKeyAndComputed.Count() - 1)
|
||||
sbColumnList.Append(", ");
|
||||
}
|
||||
|
||||
var sbParameterList = new StringBuilder(null);
|
||||
for (var i = 0; i < allPropertiesExceptKeyAndComputed.Count(); i++)
|
||||
{
|
||||
var property = allPropertiesExceptKeyAndComputed.ElementAt(i);
|
||||
sbParameterList.AppendFormat("@{0}", property.Name);
|
||||
if (i < allPropertiesExceptKeyAndComputed.Count() - 1)
|
||||
sbParameterList.Append(", ");
|
||||
}
|
||||
ISqlAdapter adapter = GetFormatter(connection);
|
||||
int id = await adapter.InsertAsync(connection, transaction, commandTimeout, name, sbColumnList.ToString(), sbParameterList.ToString(), keyProperties, entityToInsert);
|
||||
return id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates entity in table "Ts", checks if the entity is modified if the entity is tracked by the Get() extension.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type to be updated</typeparam>
|
||||
/// <param name="connection">Open SqlConnection</param>
|
||||
/// <param name="entityToUpdate">Entity to be updated</param>
|
||||
/// <returns>true if updated, false if not found or not modified (tracked entities)</returns>
|
||||
public static bool Update<T>(this IDbConnection connection, T entityToUpdate, IDbTransaction transaction = null, int? commandTimeout = null) where T : class
|
||||
{
|
||||
var proxy = entityToUpdate as IProxy;
|
||||
if (proxy != null)
|
||||
{
|
||||
if (!proxy.IsDirty) return false;
|
||||
}
|
||||
|
||||
var type = typeof(T);
|
||||
|
||||
var keyProperties = KeyPropertiesCache(type);
|
||||
if (!keyProperties.Any())
|
||||
throw new ArgumentException("Entity must have at least one [Key] property");
|
||||
|
||||
var name = GetTableName(type);
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendFormat("update {0} set ", name);
|
||||
|
||||
var allProperties = TypePropertiesCache(type);
|
||||
var computedProperties = ComputedPropertiesCache(type);
|
||||
var nonIdProps = allProperties.Except(keyProperties.Union(computedProperties));
|
||||
|
||||
for (var i = 0; i < nonIdProps.Count(); i++)
|
||||
{
|
||||
var property = nonIdProps.ElementAt(i);
|
||||
sb.AppendFormat("{0} = @{1}", property.Name, property.Name);
|
||||
if (i < nonIdProps.Count() - 1)
|
||||
sb.AppendFormat(", ");
|
||||
}
|
||||
sb.Append(" where ");
|
||||
for (var i = 0; i < keyProperties.Count(); i++)
|
||||
{
|
||||
var property = keyProperties.ElementAt(i);
|
||||
sb.AppendFormat("{0} = @{1}", property.Name, property.Name);
|
||||
if (i < keyProperties.Count() - 1)
|
||||
sb.AppendFormat(" and ");
|
||||
}
|
||||
var updated = connection.Execute(sb.ToString(), entityToUpdate, commandTimeout: commandTimeout, transaction: transaction);
|
||||
return updated > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates entity in table "Ts" asynchronously using .NET 4.5 Task, checks if the entity is modified if the entity is tracked by the Get() extension.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type to be updated</typeparam>
|
||||
/// <param name="connection">Open SqlConnection</param>
|
||||
/// <param name="entityToUpdate">Entity to be updated</param>
|
||||
/// <returns>true if updated, false if not found or not modified (tracked entities)</returns>
|
||||
public static async Task<bool> UpdateAsync<T>(this IDbConnection connection, T entityToUpdate, IDbTransaction transaction = null, int? commandTimeout = null) where T : class
|
||||
{
|
||||
var proxy = entityToUpdate as IProxy;
|
||||
if (proxy != null)
|
||||
{
|
||||
if (!proxy.IsDirty) return false;
|
||||
}
|
||||
|
||||
var type = typeof(T);
|
||||
|
||||
var keyProperties = KeyPropertiesCache(type);
|
||||
if (!keyProperties.Any())
|
||||
throw new ArgumentException("Entity must have at least one [Key] property");
|
||||
|
||||
var name = GetTableName(type);
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendFormat("update {0} set ", name);
|
||||
|
||||
var allProperties = TypePropertiesCache(type);
|
||||
var computedProperties = ComputedPropertiesCache(type);
|
||||
var nonIdProps = allProperties.Except(keyProperties.Union(computedProperties));
|
||||
|
||||
for (var i = 0; i < nonIdProps.Count(); i++)
|
||||
{
|
||||
var property = nonIdProps.ElementAt(i);
|
||||
sb.AppendFormat("{0} = @{1}", property.Name, property.Name);
|
||||
if (i < nonIdProps.Count() - 1)
|
||||
sb.AppendFormat(", ");
|
||||
}
|
||||
sb.Append(" where ");
|
||||
for (var i = 0; i < keyProperties.Count(); i++)
|
||||
{
|
||||
var property = keyProperties.ElementAt(i);
|
||||
sb.AppendFormat("{0} = @{1}", property.Name, property.Name);
|
||||
if (i < keyProperties.Count() - 1)
|
||||
sb.AppendFormat(" and ");
|
||||
}
|
||||
var updated = await connection.ExecuteAsync(sb.ToString(), entityToUpdate, commandTimeout: commandTimeout, transaction: transaction);
|
||||
return updated > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete entity in table "Ts".
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of entity</typeparam>
|
||||
/// <param name="connection">Open SqlConnection</param>
|
||||
/// <param name="entityToDelete">Entity to delete</param>
|
||||
/// <returns>true if deleted, false if not found</returns>
|
||||
public static bool Delete<T>(this IDbConnection connection, T entityToDelete, IDbTransaction transaction = null, int? commandTimeout = null) where T : class
|
||||
{
|
||||
if (entityToDelete == null)
|
||||
throw new ArgumentException("Cannot Delete null Object", "entityToDelete");
|
||||
|
||||
var type = typeof(T);
|
||||
|
||||
var keyProperties = KeyPropertiesCache(type);
|
||||
if (keyProperties.Count() == 0)
|
||||
throw new ArgumentException("Entity must have at least one [Key] property");
|
||||
|
||||
var name = GetTableName(type);
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendFormat("delete from {0} where ", name);
|
||||
|
||||
for (var i = 0; i < keyProperties.Count(); i++)
|
||||
{
|
||||
var property = keyProperties.ElementAt(i);
|
||||
sb.AppendFormat("{0} = @{1}", property.Name, property.Name);
|
||||
if (i < keyProperties.Count() - 1)
|
||||
sb.AppendFormat(" and ");
|
||||
}
|
||||
var deleted = connection.Execute(sb.ToString(), entityToDelete, transaction: transaction, commandTimeout: commandTimeout);
|
||||
return deleted > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete entity in table "Ts" asynchronously using .NET 4.5 Task.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of entity</typeparam>
|
||||
/// <param name="connection">Open SqlConnection</param>
|
||||
/// <param name="entityToDelete">Entity to delete</param>
|
||||
/// <returns>true if deleted, false if not found</returns>
|
||||
public static async Task<bool> DeleteAsync<T>(this IDbConnection connection, T entityToDelete, IDbTransaction transaction = null, int? commandTimeout = null) where T : class
|
||||
{
|
||||
if (entityToDelete == null)
|
||||
throw new ArgumentException("Cannot Delete null Object", "entityToDelete");
|
||||
|
||||
var type = typeof(T);
|
||||
|
||||
var keyProperties = KeyPropertiesCache(type);
|
||||
if (keyProperties.Count() == 0)
|
||||
throw new ArgumentException("Entity must have at least one [Key] property");
|
||||
|
||||
var name = GetTableName(type);
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendFormat("delete from {0} where ", name);
|
||||
|
||||
for (var i = 0; i < keyProperties.Count(); i++)
|
||||
{
|
||||
var property = keyProperties.ElementAt(i);
|
||||
sb.AppendFormat("{0} = @{1}", property.Name, property.Name);
|
||||
if (i < keyProperties.Count() - 1)
|
||||
sb.AppendFormat(" and ");
|
||||
}
|
||||
var deleted = await connection.ExecuteAsync(sb.ToString(), entityToDelete, transaction: transaction, commandTimeout: commandTimeout);
|
||||
return deleted > 0;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Delete all entities in the table related to the type T.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of entity</typeparam>
|
||||
/// <param name="connection">Open SqlConnection</param>
|
||||
/// <returns>true if deleted, false if none found</returns>
|
||||
public static bool DeleteAll<T>(this IDbConnection connection, IDbTransaction transaction = null, int? commandTimeout = null) where T : class
|
||||
{
|
||||
var type = typeof(T);
|
||||
var name = GetTableName(type);
|
||||
var statement = String.Format("delete from {0}", name);
|
||||
var deleted = connection.Execute(statement, null, transaction: transaction, commandTimeout: commandTimeout);
|
||||
return deleted > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete all entities in the table related to the type T asynchronously using .NET 4.5 Task.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of entity</typeparam>
|
||||
/// <param name="connection">Open SqlConnection</param>
|
||||
/// <returns>true if deleted, false if none found</returns>
|
||||
public static async Task<bool> DeleteAllAsync<T>(this IDbConnection connection, IDbTransaction transaction = null, int? commandTimeout = null) where T : class
|
||||
{
|
||||
var type = typeof(T);
|
||||
var name = GetTableName(type);
|
||||
var statement = String.Format("delete from {0}", name);
|
||||
var deleted = await connection.ExecuteAsync(statement, null, transaction: transaction, commandTimeout: commandTimeout);
|
||||
return deleted > 0;
|
||||
}
|
||||
|
||||
|
||||
public static ISqlAdapter GetFormatter(IDbConnection connection)
|
||||
{
|
||||
string name = connection.GetType().Name.ToLower();
|
||||
if (!AdapterDictionary.ContainsKey(name))
|
||||
return new SqlServerAdapter();
|
||||
return AdapterDictionary[name];
|
||||
}
|
||||
|
||||
class ProxyGenerator
|
||||
{
|
||||
private static readonly Dictionary<Type, object> TypeCache = new Dictionary<Type, object>();
|
||||
|
||||
private static AssemblyBuilder GetAsmBuilder(string name)
|
||||
{
|
||||
var assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(new AssemblyName { Name = name },
|
||||
AssemblyBuilderAccess.Run); //NOTE: to save, use RunAndSave
|
||||
|
||||
return assemblyBuilder;
|
||||
}
|
||||
|
||||
public static T GetClassProxy<T>()
|
||||
{
|
||||
// A class proxy could be implemented if all properties are virtual
|
||||
// otherwise there is a pretty dangerous case where internal actions will not update dirty tracking
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
public static T GetInterfaceProxy<T>()
|
||||
{
|
||||
Type typeOfT = typeof(T);
|
||||
|
||||
object k;
|
||||
if (TypeCache.TryGetValue(typeOfT, out k))
|
||||
{
|
||||
return (T)k;
|
||||
}
|
||||
var assemblyBuilder = GetAsmBuilder(typeOfT.Name);
|
||||
|
||||
var moduleBuilder = assemblyBuilder.DefineDynamicModule("SqlMapperExtensions." + typeOfT.Name); //NOTE: to save, add "asdasd.dll" parameter
|
||||
|
||||
var interfaceType = typeof(Dapper.Contrib.Extensions.SqlMapperExtensions.IProxy);
|
||||
var typeBuilder = moduleBuilder.DefineType(typeOfT.Name + "_" + Guid.NewGuid(),
|
||||
TypeAttributes.Public | TypeAttributes.Class);
|
||||
typeBuilder.AddInterfaceImplementation(typeOfT);
|
||||
typeBuilder.AddInterfaceImplementation(interfaceType);
|
||||
|
||||
//create our _isDirty field, which implements IProxy
|
||||
var setIsDirtyMethod = CreateIsDirtyProperty(typeBuilder);
|
||||
|
||||
// Generate a field for each property, which implements the T
|
||||
foreach (var property in typeof(T).GetProperties())
|
||||
{
|
||||
var isId = property.GetCustomAttributes(true).Any(a => a is KeyAttribute);
|
||||
CreateProperty<T>(typeBuilder, property.Name, property.PropertyType, setIsDirtyMethod, isId);
|
||||
}
|
||||
|
||||
var generatedType = typeBuilder.CreateType();
|
||||
|
||||
//assemblyBuilder.Save(name + ".dll"); //NOTE: to save, uncomment
|
||||
|
||||
var generatedObject = Activator.CreateInstance(generatedType);
|
||||
|
||||
TypeCache.Add(typeOfT, generatedObject);
|
||||
return (T)generatedObject;
|
||||
}
|
||||
|
||||
|
||||
private static MethodInfo CreateIsDirtyProperty(TypeBuilder typeBuilder)
|
||||
{
|
||||
var propType = typeof(bool);
|
||||
var field = typeBuilder.DefineField("_" + "IsDirty", propType, FieldAttributes.Private);
|
||||
var property = typeBuilder.DefineProperty("IsDirty",
|
||||
System.Reflection.PropertyAttributes.None,
|
||||
propType,
|
||||
new Type[] { propType });
|
||||
|
||||
const MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.NewSlot | MethodAttributes.SpecialName |
|
||||
MethodAttributes.Final | MethodAttributes.Virtual | MethodAttributes.HideBySig;
|
||||
|
||||
// Define the "get" and "set" accessor methods
|
||||
var currGetPropMthdBldr = typeBuilder.DefineMethod("get_" + "IsDirty",
|
||||
getSetAttr,
|
||||
propType,
|
||||
Type.EmptyTypes);
|
||||
var currGetIL = currGetPropMthdBldr.GetILGenerator();
|
||||
currGetIL.Emit(OpCodes.Ldarg_0);
|
||||
currGetIL.Emit(OpCodes.Ldfld, field);
|
||||
currGetIL.Emit(OpCodes.Ret);
|
||||
var currSetPropMthdBldr = typeBuilder.DefineMethod("set_" + "IsDirty",
|
||||
getSetAttr,
|
||||
null,
|
||||
new Type[] { propType });
|
||||
var currSetIL = currSetPropMthdBldr.GetILGenerator();
|
||||
currSetIL.Emit(OpCodes.Ldarg_0);
|
||||
currSetIL.Emit(OpCodes.Ldarg_1);
|
||||
currSetIL.Emit(OpCodes.Stfld, field);
|
||||
currSetIL.Emit(OpCodes.Ret);
|
||||
|
||||
property.SetGetMethod(currGetPropMthdBldr);
|
||||
property.SetSetMethod(currSetPropMthdBldr);
|
||||
var getMethod = typeof(Dapper.Contrib.Extensions.SqlMapperExtensions.IProxy).GetMethod("get_" + "IsDirty");
|
||||
var setMethod = typeof(Dapper.Contrib.Extensions.SqlMapperExtensions.IProxy).GetMethod("set_" + "IsDirty");
|
||||
typeBuilder.DefineMethodOverride(currGetPropMthdBldr, getMethod);
|
||||
typeBuilder.DefineMethodOverride(currSetPropMthdBldr, setMethod);
|
||||
|
||||
return currSetPropMthdBldr;
|
||||
}
|
||||
|
||||
private static void CreateProperty<T>(TypeBuilder typeBuilder, string propertyName, Type propType, MethodInfo setIsDirtyMethod, bool isIdentity)
|
||||
{
|
||||
//Define the field and the property
|
||||
var field = typeBuilder.DefineField("_" + propertyName, propType, FieldAttributes.Private);
|
||||
var property = typeBuilder.DefineProperty(propertyName,
|
||||
System.Reflection.PropertyAttributes.None,
|
||||
propType,
|
||||
new Type[] { propType });
|
||||
|
||||
const MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.Virtual |
|
||||
MethodAttributes.HideBySig;
|
||||
|
||||
// Define the "get" and "set" accessor methods
|
||||
var currGetPropMthdBldr = typeBuilder.DefineMethod("get_" + propertyName,
|
||||
getSetAttr,
|
||||
propType,
|
||||
Type.EmptyTypes);
|
||||
|
||||
var currGetIL = currGetPropMthdBldr.GetILGenerator();
|
||||
currGetIL.Emit(OpCodes.Ldarg_0);
|
||||
currGetIL.Emit(OpCodes.Ldfld, field);
|
||||
currGetIL.Emit(OpCodes.Ret);
|
||||
|
||||
var currSetPropMthdBldr = typeBuilder.DefineMethod("set_" + propertyName,
|
||||
getSetAttr,
|
||||
null,
|
||||
new Type[] { propType });
|
||||
|
||||
//store value in private field and set the isdirty flag
|
||||
var currSetIL = currSetPropMthdBldr.GetILGenerator();
|
||||
currSetIL.Emit(OpCodes.Ldarg_0);
|
||||
currSetIL.Emit(OpCodes.Ldarg_1);
|
||||
currSetIL.Emit(OpCodes.Stfld, field);
|
||||
currSetIL.Emit(OpCodes.Ldarg_0);
|
||||
currSetIL.Emit(OpCodes.Ldc_I4_1);
|
||||
currSetIL.Emit(OpCodes.Call, setIsDirtyMethod);
|
||||
currSetIL.Emit(OpCodes.Ret);
|
||||
|
||||
//TODO: Should copy all attributes defined by the interface?
|
||||
if (isIdentity)
|
||||
{
|
||||
var keyAttribute = typeof(KeyAttribute);
|
||||
var myConstructorInfo = keyAttribute.GetConstructor(new Type[] { });
|
||||
var attributeBuilder = new CustomAttributeBuilder(myConstructorInfo, new object[] { });
|
||||
property.SetCustomAttribute(attributeBuilder);
|
||||
}
|
||||
|
||||
property.SetGetMethod(currGetPropMthdBldr);
|
||||
property.SetSetMethod(currSetPropMthdBldr);
|
||||
var getMethod = typeof(T).GetMethod("get_" + propertyName);
|
||||
var setMethod = typeof(T).GetMethod("set_" + propertyName);
|
||||
typeBuilder.DefineMethodOverride(currGetPropMthdBldr, getMethod);
|
||||
typeBuilder.DefineMethodOverride(currSetPropMthdBldr, setMethod);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class TableAttribute : Attribute
|
||||
{
|
||||
public TableAttribute(string tableName)
|
||||
{
|
||||
Name = tableName;
|
||||
}
|
||||
public string Name { get; private set; }
|
||||
}
|
||||
|
||||
// do not want to depend on data annotations that is not in client profile
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class KeyAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class WriteAttribute : Attribute
|
||||
{
|
||||
public WriteAttribute(bool write)
|
||||
{
|
||||
Write = write;
|
||||
}
|
||||
public bool Write { get; private set; }
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class ComputedAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public interface ISqlAdapter
|
||||
{
|
||||
int Insert(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, String tableName, string columnList, string parameterList, IEnumerable<PropertyInfo> keyProperties, object entityToInsert);
|
||||
Task<int> InsertAsync(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, String tableName, string columnList, string parameterList, IEnumerable<PropertyInfo> keyProperties, object entityToInsert);
|
||||
}
|
||||
|
||||
public class SqlServerAdapter : ISqlAdapter
|
||||
{
|
||||
public int Insert(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, String tableName, string columnList, string parameterList, IEnumerable<PropertyInfo> keyProperties, object entityToInsert)
|
||||
{
|
||||
string cmd = String.Format("insert into {0} ({1}) values ({2})", tableName, columnList, parameterList);
|
||||
|
||||
connection.Execute(cmd, entityToInsert, transaction: transaction, commandTimeout: commandTimeout);
|
||||
|
||||
//NOTE: would prefer to use IDENT_CURRENT('tablename') or IDENT_SCOPE but these are not available on SQLCE
|
||||
var r = connection.Query("select @@IDENTITY id", transaction: transaction, commandTimeout: commandTimeout);
|
||||
int id = (int)r.First().id;
|
||||
if (keyProperties.Any())
|
||||
keyProperties.First().SetValue(entityToInsert, id, null);
|
||||
return id;
|
||||
}
|
||||
|
||||
public async Task<int> InsertAsync(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, String tableName, string columnList, string parameterList, IEnumerable<PropertyInfo> keyProperties, object entityToInsert)
|
||||
{
|
||||
string cmd = String.Format("insert into {0} ({1}) values ({2})", tableName, columnList, parameterList);
|
||||
|
||||
await connection.ExecuteAsync(cmd, entityToInsert, transaction: transaction, commandTimeout: commandTimeout);
|
||||
|
||||
//NOTE: would prefer to use IDENT_CURRENT('tablename') or IDENT_SCOPE but these are not available on SQLCE
|
||||
var r = await connection.QueryAsync<dynamic>("select @@IDENTITY id", transaction: transaction, commandTimeout: commandTimeout);
|
||||
int id = (int)r.First().id;
|
||||
if (keyProperties.Any())
|
||||
keyProperties.First().SetValue(entityToInsert, id, null);
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
public class PostgresAdapter : ISqlAdapter
|
||||
{
|
||||
public int Insert(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, String tableName, string columnList, string parameterList, IEnumerable<PropertyInfo> keyProperties, object entityToInsert)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendFormat("insert into {0} ({1}) values ({2})", tableName, columnList, parameterList);
|
||||
|
||||
// If no primary key then safe to assume a join table with not too much data to return
|
||||
if (!keyProperties.Any())
|
||||
sb.Append(" RETURNING *");
|
||||
else
|
||||
{
|
||||
sb.Append(" RETURNING ");
|
||||
bool first = true;
|
||||
foreach (var property in keyProperties)
|
||||
{
|
||||
if (!first)
|
||||
sb.Append(", ");
|
||||
first = false;
|
||||
sb.Append(property.Name);
|
||||
}
|
||||
}
|
||||
|
||||
var results = connection.Query(sb.ToString(), entityToInsert, transaction: transaction, commandTimeout: commandTimeout);
|
||||
|
||||
// Return the key by assinging the corresponding property in the object - by product is that it supports compound primary keys
|
||||
int id = 0;
|
||||
foreach (var p in keyProperties)
|
||||
{
|
||||
var value = ((IDictionary<string, object>)results.First())[p.Name.ToLower()];
|
||||
p.SetValue(entityToInsert, value, null);
|
||||
if (id == 0)
|
||||
id = Convert.ToInt32(value);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
public async Task<int> InsertAsync(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, String tableName, string columnList, string parameterList, IEnumerable<PropertyInfo> keyProperties, object entityToInsert)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendFormat("insert into {0} ({1}) values ({2})", tableName, columnList, parameterList);
|
||||
|
||||
// If no primary key then safe to assume a join table with not too much data to return
|
||||
if (!keyProperties.Any())
|
||||
sb.Append(" RETURNING *");
|
||||
else
|
||||
{
|
||||
sb.Append(" RETURNING ");
|
||||
bool first = true;
|
||||
foreach (var property in keyProperties)
|
||||
{
|
||||
if (!first)
|
||||
sb.Append(", ");
|
||||
first = false;
|
||||
sb.Append(property.Name);
|
||||
}
|
||||
}
|
||||
|
||||
var results = await connection.QueryAsync<dynamic>(sb.ToString(), entityToInsert, transaction: transaction, commandTimeout: commandTimeout);
|
||||
|
||||
// Return the key by assinging the corresponding property in the object - by product is that it supports compound primary keys
|
||||
int id = 0;
|
||||
foreach (var p in keyProperties)
|
||||
{
|
||||
var value = ((IDictionary<string, object>)results.First())[p.Name.ToLower()];
|
||||
p.SetValue(entityToInsert, value, null);
|
||||
if (id == 0)
|
||||
id = Convert.ToInt32(value);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
public class SQLiteAdapter : ISqlAdapter
|
||||
{
|
||||
public int Insert(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, String tableName, string columnList, string parameterList, IEnumerable<PropertyInfo> keyProperties, object entityToInsert)
|
||||
{
|
||||
string cmd = String.Format("insert into {0} ({1}) values ({2})", tableName, columnList, parameterList);
|
||||
|
||||
connection.Execute(cmd, entityToInsert, transaction: transaction, commandTimeout: commandTimeout);
|
||||
|
||||
var r = connection.Query("select last_insert_rowid() id", transaction: transaction, commandTimeout: commandTimeout);
|
||||
int id = (int)r.First().id;
|
||||
if (keyProperties.Any())
|
||||
keyProperties.First().SetValue(entityToInsert, id, null);
|
||||
return id;
|
||||
}
|
||||
|
||||
public async Task<int> InsertAsync(IDbConnection connection, IDbTransaction transaction, int? commandTimeout, String tableName, string columnList, string parameterList, IEnumerable<PropertyInfo> keyProperties, object entityToInsert)
|
||||
{
|
||||
string cmd = String.Format("insert into {0} ({1}) values ({2})", tableName, columnList, parameterList);
|
||||
|
||||
await connection.ExecuteAsync(cmd, entityToInsert, transaction: transaction, commandTimeout: commandTimeout);
|
||||
|
||||
var r = await connection.QueryAsync<dynamic>("select last_insert_rowid() id", transaction: transaction, commandTimeout: commandTimeout);
|
||||
int id = (int)r.First().id;
|
||||
if (keyProperties.Any())
|
||||
keyProperties.First().SetValue(entityToInsert, id, null);
|
||||
return id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
|
||||
</startup>
|
||||
</configuration>
|
||||
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Dapper.Contrib.Tests_NET45
|
||||
{
|
||||
/// <summary>
|
||||
/// Assert extensions borrowed from Sam's code in DapperTests
|
||||
/// </summary>
|
||||
static class Assert
|
||||
{
|
||||
public static void IsEqualTo<T>(this T obj, T other)
|
||||
{
|
||||
if (!obj.Equals(other))
|
||||
{
|
||||
throw new ApplicationException(string.Format("{0} should be equals to {1}", obj, other));
|
||||
}
|
||||
}
|
||||
|
||||
public static void IsSequenceEqualTo<T>(this IEnumerable<T> obj, IEnumerable<T> other)
|
||||
{
|
||||
if (!obj.SequenceEqual(other))
|
||||
{
|
||||
throw new ApplicationException(string.Format("{0} should be equals 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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{7A85178F-4ADC-4E4C-BF08-17FC99488A9A}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Dapper.Contrib.Tests_NET45</RootNamespace>
|
||||
<AssemblyName>Dapper.Contrib.Tests NET45</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Data.SqlServerCe, Version=4.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Assert.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Tests.cs" />
|
||||
<Compile Include="TestsAsync.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Dapper NET45\Dapper NET45.csproj">
|
||||
<Project>{0fff5bc7-0a4b-4d87-835e-4fad70937507}</Project>
|
||||
<Name>Dapper NET45</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Dapper.Contrib NET45\Dapper.Contrib NET45.csproj">
|
||||
<Project>{302ec82f-a81b-48c5-b653-b5c75d2bd103}</Project>
|
||||
<Name>Dapper.Contrib NET45</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Dapper.SqlBuilder\Dapper.SqlBuilder.csproj">
|
||||
<Project>{bf782ef1-2b0f-42fa-9dd0-928454a94c6d}</Project>
|
||||
<Name>Dapper.SqlBuilder</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
@@ -0,0 +1,95 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlServerCe;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Dapper.Contrib.Tests_NET45
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Setup();
|
||||
RunTests();
|
||||
Setup();
|
||||
RunAsyncTests();
|
||||
Console.ReadKey();
|
||||
}
|
||||
|
||||
private static void Setup()
|
||||
{
|
||||
var projLoc = Assembly.GetAssembly(typeof(Program)).Location;
|
||||
var projFolder = Path.GetDirectoryName(projLoc);
|
||||
|
||||
if (File.Exists(projFolder + "\\Test.sdf"))
|
||||
File.Delete(projFolder + "\\Test.sdf");
|
||||
var connectionString = "Data Source = " + projFolder + "\\Test.sdf;";
|
||||
var engine = new SqlCeEngine(connectionString);
|
||||
engine.CreateDatabase();
|
||||
using (var connection = new SqlCeConnection(connectionString))
|
||||
{
|
||||
connection.Open();
|
||||
connection.Execute(@" create table Users (Id int IDENTITY(1,1) not null, Name nvarchar(100) not null, Age int not null) ");
|
||||
connection.Execute(@" create table Automobiles (Id int IDENTITY(1,1) not null, Name nvarchar(100) not null) ");
|
||||
connection.Execute(@" create table Results (Id int IDENTITY(1,1) not null, Name nvarchar(100) not null, [Order] int not null) ");
|
||||
}
|
||||
Console.WriteLine("Created database");
|
||||
}
|
||||
|
||||
private static void RunTests()
|
||||
{
|
||||
var tester = new Tests();
|
||||
foreach (var method in typeof(Tests).GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
|
||||
{
|
||||
if (method.ReturnType != typeof(Task))
|
||||
{
|
||||
Console.Write("Running " + method.Name);
|
||||
method.Invoke(tester, null);
|
||||
Console.WriteLine(" - OK!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void RunAsyncTests()
|
||||
{
|
||||
var tester = new TestsAsync();
|
||||
|
||||
Console.Write("Running TableNameAsync");
|
||||
Task.WaitAll(tester.TableNameAsync());
|
||||
Console.WriteLine(" - OK!");
|
||||
|
||||
Console.Write("Running TestSimpleGetAsync");
|
||||
Task.WaitAll(tester.TestSimpleGetAsync());
|
||||
Console.WriteLine(" - OK!");
|
||||
|
||||
Console.Write("Running InsertGetUpdateAsync");
|
||||
Task.WaitAll(tester.InsertGetUpdateAsync());
|
||||
Console.WriteLine(" - OK!");
|
||||
|
||||
Console.Write("Running InsertCheckKeyAsync");
|
||||
Task.WaitAll(tester.InsertCheckKeyAsync());
|
||||
Console.WriteLine(" - OK!");
|
||||
|
||||
Console.Write("Running BuilderSelectClauseAsync");
|
||||
Task.WaitAll(tester.BuilderSelectClauseAsync());
|
||||
Console.WriteLine(" - OK!");
|
||||
|
||||
Console.Write("Running BuilderTemplateWOCompositionAsync");
|
||||
Task.WaitAll(tester.BuilderTemplateWOCompositionAsync());
|
||||
Console.WriteLine(" - OK!");
|
||||
|
||||
Console.Write("Running InsertFieldWithReservedNameAsync");
|
||||
Task.WaitAll(tester.InsertFieldWithReservedNameAsync());
|
||||
Console.WriteLine(" - OK!");
|
||||
|
||||
Console.Write("Running DeleteAllAsync");
|
||||
Task.WaitAll(tester.DeleteAllAsync());
|
||||
Console.WriteLine(" - OK!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("Dapper.Contrib.Tests NET45")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("Dapper.Contrib.Tests NET45")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2014")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("2d6cdd3d-b94b-4cd4-842a-60c6e4c93c5c")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
@@ -0,0 +1,198 @@
|
||||
using System.Data;
|
||||
using System.Data.SqlServerCe;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Dapper.Contrib.Extensions;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using Dapper;
|
||||
|
||||
|
||||
namespace Dapper.Contrib.Tests_NET45
|
||||
{
|
||||
public interface IUser
|
||||
{
|
||||
[Key]
|
||||
int Id { get; set; }
|
||||
string Name { get; set; }
|
||||
int Age { get; set; }
|
||||
}
|
||||
|
||||
public class User : IUser
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public int Age { get; set; }
|
||||
}
|
||||
|
||||
[Table("Automobiles")]
|
||||
public class Car
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
[Table("Results")]
|
||||
public class Result
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public int Order { get; set; }
|
||||
}
|
||||
|
||||
public class Tests
|
||||
{
|
||||
private IDbConnection GetOpenConnection()
|
||||
{
|
||||
var projLoc = Assembly.GetAssembly(GetType()).Location;
|
||||
var projFolder = Path.GetDirectoryName(projLoc);
|
||||
|
||||
var connection = new SqlCeConnection("Data Source = " + projFolder + "\\Test.sdf;");
|
||||
connection.Open();
|
||||
return connection;
|
||||
}
|
||||
|
||||
public void TableName()
|
||||
{
|
||||
using (var connection = GetOpenConnection())
|
||||
{
|
||||
// tests against "Automobiles" table (Table attribute)
|
||||
connection.Insert(new Car {Name = "Volvo"});
|
||||
connection.Get<Car>(1).Name.IsEqualTo("Volvo");
|
||||
connection.Update(new Car() {Id = 1, Name = "Saab"}).IsEqualTo(true);
|
||||
connection.Get<Car>(1).Name.IsEqualTo("Saab");
|
||||
connection.Delete(new Car() {Id = 1}).IsEqualTo(true);
|
||||
connection.Get<Car>(1).IsNull();
|
||||
}
|
||||
}
|
||||
|
||||
public void TestSimpleGet()
|
||||
{
|
||||
using (var connection = GetOpenConnection())
|
||||
{
|
||||
var id = connection.Insert(new User { Name = "Adama", Age = 10 });
|
||||
var user = connection.Get<User>(id);
|
||||
user.Id.IsEqualTo((int)id);
|
||||
user.Name.IsEqualTo("Adama");
|
||||
connection.Delete(user);
|
||||
}
|
||||
}
|
||||
|
||||
public void InsertGetUpdate()
|
||||
{
|
||||
using (var connection = GetOpenConnection())
|
||||
{
|
||||
connection.Get<User>(3).IsNull();
|
||||
|
||||
var id = connection.Insert(new User {Name = "Adam", Age = 10});
|
||||
|
||||
//get a user with "isdirty" tracking
|
||||
var user = connection.Get<IUser>(id);
|
||||
user.Name.IsEqualTo("Adam");
|
||||
connection.Update(user).IsEqualTo(false); //returns false if not updated, based on tracking
|
||||
user.Name = "Bob";
|
||||
connection.Update(user).IsEqualTo(true); //returns true if updated, based on tracking
|
||||
user = connection.Get<IUser>(id);
|
||||
user.Name.IsEqualTo("Bob");
|
||||
|
||||
//get a user with no tracking
|
||||
var notrackedUser = connection.Get<User>(id);
|
||||
notrackedUser.Name.IsEqualTo("Bob");
|
||||
connection.Update(notrackedUser).IsEqualTo(true); //returns true, even though user was not changed
|
||||
notrackedUser.Name = "Cecil";
|
||||
connection.Update(notrackedUser).IsEqualTo(true);
|
||||
connection.Get<User>(id).Name.IsEqualTo("Cecil");
|
||||
|
||||
connection.Query<User>("select * from Users").Count().IsEqualTo(1);
|
||||
connection.Delete(user).IsEqualTo(true);
|
||||
connection.Query<User>("select * from Users").Count().IsEqualTo(0);
|
||||
|
||||
connection.Update(notrackedUser).IsEqualTo(false); //returns false, user not found
|
||||
}
|
||||
}
|
||||
|
||||
public void InsertCheckKey()
|
||||
{
|
||||
using (var connection = GetOpenConnection())
|
||||
{
|
||||
connection.Get<IUser>(3).IsNull();
|
||||
User user = new User { Name = "Adamb", Age = 10 };
|
||||
int id = (int)connection.Insert(user);
|
||||
user.Id.IsEqualTo(id);
|
||||
}
|
||||
}
|
||||
|
||||
public void BuilderSelectClause()
|
||||
{
|
||||
using (var connection = GetOpenConnection())
|
||||
{
|
||||
var rand = new Random(8675309);
|
||||
var data = new List<User>();
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
var nU = new User { Age = rand.Next(70), Id = i, Name = Guid.NewGuid().ToString() };
|
||||
data.Add(nU);
|
||||
nU.Id = (int)connection.Insert<User>(nU);
|
||||
}
|
||||
|
||||
var builder = new SqlBuilder();
|
||||
var justId = builder.AddTemplate("SELECT /**select**/ FROM Users");
|
||||
var all = builder.AddTemplate("SELECT Name, /**select**/, Age FROM Users");
|
||||
|
||||
builder.Select("Id");
|
||||
|
||||
var ids = connection.Query<int>(justId.RawSql, justId.Parameters);
|
||||
var users = connection.Query<User>(all.RawSql, all.Parameters);
|
||||
|
||||
foreach (var u in data)
|
||||
{
|
||||
if (!ids.Any(i => u.Id == i)) throw new Exception("Missing ids in select");
|
||||
if (!users.Any(a => a.Id == u.Id && a.Name == u.Name && a.Age == u.Age)) throw new Exception("Missing users in select");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void BuilderTemplateWOComposition()
|
||||
{
|
||||
var builder = new SqlBuilder();
|
||||
var template = builder.AddTemplate("SELECT COUNT(*) FROM Users WHERE Age = @age", new {age = 5});
|
||||
|
||||
if (template.RawSql == null) throw new Exception("RawSql null");
|
||||
if (template.Parameters == null) throw new Exception("Parameters null");
|
||||
|
||||
using (var connection = GetOpenConnection())
|
||||
{
|
||||
connection.Insert(new User { Age = 5, Name = "Testy McTestington" });
|
||||
|
||||
if (connection.Query<int>(template.RawSql, template.Parameters).Single() != 1)
|
||||
throw new Exception("Query failed");
|
||||
}
|
||||
}
|
||||
|
||||
public void InsertFieldWithReservedName()
|
||||
{
|
||||
using (var conneciton = GetOpenConnection())
|
||||
{
|
||||
var id = conneciton.Insert(new Result() { Name = "Adam", Order = 1 });
|
||||
|
||||
var result = conneciton.Get<Result>(id);
|
||||
result.Order.IsEqualTo(1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void DeleteAll()
|
||||
{
|
||||
using (var connection = GetOpenConnection())
|
||||
{
|
||||
var id1 = connection.Insert(new User() { Name = "Alice", Age = 32 });
|
||||
var id2 = connection.Insert(new User() { Name = "Bob", Age = 33 });
|
||||
connection.DeleteAll<User>();
|
||||
connection.Get<User>(id1).IsNull();
|
||||
connection.Get<User>(id2).IsNull();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
using System.Data;
|
||||
using System.Data.SqlServerCe;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Dapper.Contrib.Extensions;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using Dapper;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
namespace Dapper.Contrib.Tests_NET45
|
||||
{
|
||||
public class TestsAsync
|
||||
{
|
||||
private IDbConnection GetOpenConnection()
|
||||
{
|
||||
var projLoc = Assembly.GetAssembly(GetType()).Location;
|
||||
var projFolder = Path.GetDirectoryName(projLoc);
|
||||
|
||||
var connection = new SqlCeConnection("Data Source = " + projFolder + "\\Test.sdf;");
|
||||
connection.Open();
|
||||
return connection;
|
||||
}
|
||||
|
||||
public async Task TableNameAsync()
|
||||
{
|
||||
using (var connection = GetOpenConnection())
|
||||
{
|
||||
// tests against "Automobiles" table (Table attribute)
|
||||
await connection.InsertAsync(new Car { Name = "Volvo" });
|
||||
(await connection.GetAsync<Car>(1)).Name.IsEqualTo("Volvo");
|
||||
(await connection.UpdateAsync(new Car() { Id = 1, Name = "Saab" })).IsEqualTo(true);
|
||||
(await connection.GetAsync<Car>(1)).Name.IsEqualTo("Saab");
|
||||
(await connection.DeleteAsync(new Car() { Id = 1 })).IsEqualTo(true);
|
||||
(await connection.GetAsync<Car>(1)).IsNull();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task TestSimpleGetAsync()
|
||||
{
|
||||
using (var connection = GetOpenConnection())
|
||||
{
|
||||
var id = await connection.InsertAsync(new User { Name = "Adama", Age = 10 });
|
||||
var user = await connection.GetAsync<User>(id);
|
||||
user.Id.IsEqualTo((int)id);
|
||||
user.Name.IsEqualTo("Adama");
|
||||
await connection.DeleteAsync(user);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task InsertGetUpdateAsync()
|
||||
{
|
||||
using (var connection = GetOpenConnection())
|
||||
{
|
||||
(await connection.GetAsync<User>(3)).IsNull();
|
||||
|
||||
var id = await connection.InsertAsync(new User { Name = "Adam", Age = 10 });
|
||||
|
||||
//get a user with "isdirty" tracking
|
||||
var user = await connection.GetAsync<IUser>(id);
|
||||
user.Name.IsEqualTo("Adam");
|
||||
(await connection.UpdateAsync(user)).IsEqualTo(false); //returns false if not updated, based on tracking
|
||||
user.Name = "Bob";
|
||||
(await connection.UpdateAsync(user)).IsEqualTo(true); //returns true if updated, based on tracking
|
||||
user = await connection.GetAsync<IUser>(id);
|
||||
user.Name.IsEqualTo("Bob");
|
||||
|
||||
//get a user with no tracking
|
||||
var notrackedUser = await connection.GetAsync<User>(id);
|
||||
notrackedUser.Name.IsEqualTo("Bob");
|
||||
(await connection.UpdateAsync(notrackedUser)).IsEqualTo(true); //returns true, even though user was not changed
|
||||
notrackedUser.Name = "Cecil";
|
||||
(await connection.UpdateAsync(notrackedUser)).IsEqualTo(true);
|
||||
(await connection.GetAsync<User>(id)).Name.IsEqualTo("Cecil");
|
||||
|
||||
(await connection.QueryAsync<User>("select * from Users")).Count().IsEqualTo(1);
|
||||
(await connection.DeleteAsync(user)).IsEqualTo(true);
|
||||
(await connection.QueryAsync<User>("select * from Users")).Count().IsEqualTo(0);
|
||||
|
||||
(await connection.UpdateAsync(notrackedUser)).IsEqualTo(false); //returns false, user not found
|
||||
}
|
||||
}
|
||||
|
||||
public async Task InsertCheckKeyAsync()
|
||||
{
|
||||
using (var connection = GetOpenConnection())
|
||||
{
|
||||
(await connection.GetAsync<IUser>(3)).IsNull();
|
||||
User user = new User { Name = "Adamb", Age = 10 };
|
||||
int id = (int)await connection.InsertAsync(user);
|
||||
user.Id.IsEqualTo(id);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task BuilderSelectClauseAsync()
|
||||
{
|
||||
using (var connection = GetOpenConnection())
|
||||
{
|
||||
var rand = new Random(8675309);
|
||||
var data = new List<User>();
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
var nU = new User { Age = rand.Next(70), Id = i, Name = Guid.NewGuid().ToString() };
|
||||
data.Add(nU);
|
||||
nU.Id = (int)await connection.InsertAsync<User>(nU);
|
||||
}
|
||||
|
||||
var builder = new SqlBuilder();
|
||||
var justId = builder.AddTemplate("SELECT /**select**/ FROM Users");
|
||||
var all = builder.AddTemplate("SELECT Name, /**select**/, Age FROM Users");
|
||||
|
||||
builder.Select("Id");
|
||||
|
||||
var ids = await connection.QueryAsync<int>(justId.RawSql, justId.Parameters);
|
||||
var users = await connection.QueryAsync<User>(all.RawSql, all.Parameters);
|
||||
|
||||
foreach (var u in data)
|
||||
{
|
||||
if (!ids.Any(i => u.Id == i)) throw new Exception("Missing ids in select");
|
||||
if (!users.Any(a => a.Id == u.Id && a.Name == u.Name && a.Age == u.Age)) throw new Exception("Missing users in select");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task BuilderTemplateWOCompositionAsync()
|
||||
{
|
||||
var builder = new SqlBuilder();
|
||||
var template = builder.AddTemplate("SELECT COUNT(*) FROM Users WHERE Age = @age", new { age = 5 });
|
||||
|
||||
if (template.RawSql == null) throw new Exception("RawSql null");
|
||||
if (template.Parameters == null) throw new Exception("Parameters null");
|
||||
|
||||
using (var connection = GetOpenConnection())
|
||||
{
|
||||
await connection.InsertAsync(new User { Age = 5, Name = "Testy McTestington" });
|
||||
|
||||
if ((await connection.QueryAsync<int>(template.RawSql, template.Parameters)).Single() != 1)
|
||||
throw new Exception("Query failed");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task InsertFieldWithReservedNameAsync()
|
||||
{
|
||||
using (var connection = GetOpenConnection())
|
||||
{
|
||||
var id = await connection.InsertAsync(new Result() { Name = "Adam", Order = 1 });
|
||||
|
||||
var result = await connection.GetAsync<Result>(id);
|
||||
result.Order.IsEqualTo(1);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DeleteAllAsync()
|
||||
{
|
||||
using (var connection = GetOpenConnection())
|
||||
{
|
||||
var id1 = await connection.InsertAsync(new User() { Name = "Alice", Age = 32 });
|
||||
var id2 = await connection.InsertAsync(new User() { Name = "Bob", Age = 33 });
|
||||
await connection.DeleteAllAsync<User>();
|
||||
(await connection.GetAsync<User>(id1)).IsNull();
|
||||
(await connection.GetAsync<User>(id2)).IsNull();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user