diff --git a/global.json b/global.json
index 4df03fcd..24864722 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,6 @@
-{
- "projects": [ "src" ],
- "sdk": {
- "version": "1.0.0-beta6"
- }
-}
+{
+ "projects": [ "src" ],
+ "sdk": {
+ "version": "1.0.0-rc1-update1"
+ }
+}
diff --git a/src/Dommel-net40/DommelMapper.cs b/src/Dommel/DommelMapper.cs
similarity index 97%
rename from src/Dommel-net40/DommelMapper.cs
rename to src/Dommel/DommelMapper.cs
index 4b759161..6a7eb80e 100644
--- a/src/Dommel-net40/DommelMapper.cs
+++ b/src/Dommel/DommelMapper.cs
@@ -1,1516 +1,1516 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
-using System.Data;
-using System.Linq;
-using System.Linq.Expressions;
-using System.Reflection;
-using System.Text;
-using Dapper;
-
-#if DNXCORE50
-using IDbTransaction = System.Data.Common.DbTransaction;
-using IDbConnection = System.Data.Common.DbConnection;
-#endif
-
-namespace Dommel
-{
- ///
- /// Simple CRUD operations for Dapper.
- ///
- public static class DommelMapper
- {
- private static readonly IDictionary _sqlBuilders = new Dictionary
- {
- { "sqlconnection", new SqlServerSqlBuilder() },
- { "sqlceconnection", new SqlServerCeSqlBuilder() },
- { "sqliteconnection", new SqliteSqlBuilder() },
- { "npgsqlconnection", new PostgresSqlBuilder() },
- { "mysqlconnection", new MySqlSqlBuilder() }
- };
-
- private static readonly IDictionary _getQueryCache = new Dictionary();
- private static readonly IDictionary _getAllQueryCache = new Dictionary();
- private static readonly IDictionary _insertQueryCache = new Dictionary();
- private static readonly IDictionary _updateQueryCache = new Dictionary();
- private static readonly IDictionary _deleteQueryCache = new Dictionary();
-
- ///
- /// Retrieves the entity of type with the specified id.
- ///
- /// The type of the entity.
- /// The connection to the database. This can either be open or closed.
- /// The id of the entity in the database.
- /// The entity with the corresponding id.
- public static TEntity Get(this IDbConnection connection, object id) where TEntity : class
- {
- var type = typeof (TEntity);
-
- string sql;
- if (!_getQueryCache.TryGetValue(type, out sql))
- {
- var tableName = Resolvers.Table(type);
- var keyProperty = Resolvers.KeyProperty(type);
- var keyColumnName = Resolvers.Column(keyProperty);
-
- sql = string.Format("select * from {0} where {1} = @Id", tableName, keyColumnName);
- _getQueryCache[type] = sql;
- }
-
- var parameters = new DynamicParameters();
- parameters.Add("Id", id);
-
- return connection.Query(sql: sql, param: parameters).FirstOrDefault();
- }
-
- ///
- /// Retrieves the entity of type with the specified id
- /// joined with the types specified as type parameters.
- ///
- /// The first type parameter. This is the source entity.
- /// The second type parameter.
- /// The return type parameter.
- /// The connection to the database. This can either be open or closed.
- /// The id of the entity in the database.
- /// The mapping to perform on the entities in the result set.
- /// The entity with the corresponding id joined with the specified types.
- public static TReturn Get(this IDbConnection connection, object id, Func map) where TReturn : class
- {
- return MultiMap(connection, map, id).FirstOrDefault();
- }
-
- ///
- /// Retrieves the entity of type with the specified id
- /// joined with the types specified as type parameters.
- ///
- /// The first type parameter. This is the source entity.
- /// The second type parameter.
- /// The third type parameter.
- /// The return type parameter.
- /// The connection to the database. This can either be open or closed.
- /// The id of the entity in the database.
- /// The mapping to perform on the entities in the result set.
- /// The entity with the corresponding id joined with the specified types.
- public static TReturn Get(this IDbConnection connection,
- object id,
- Func map) where TReturn : class
- {
- return MultiMap(connection, map, id).FirstOrDefault();
- }
-
- ///
- /// Retrieves the entity of type with the specified id
- /// joined with the types specified as type parameters.
- ///
- /// The first type parameter. This is the source entity.
- /// The second type parameter.
- /// The third type parameter.
- /// The fourth type parameter.
- /// The return type parameter.
- /// The connection to the database. This can either be open or closed.
- /// The id of the entity in the database.
- /// The mapping to perform on the entities in the result set.
- /// The entity with the corresponding id joined with the specified types.
- public static TReturn Get(this IDbConnection connection,
- object id,
- Func map) where TReturn : class
- {
- return MultiMap(connection, map, id).FirstOrDefault();
- }
-
- ///
- /// Retrieves the entity of type with the specified id
- /// joined with the types specified as type parameters.
- ///
- /// The first type parameter. This is the source entity.
- /// The second type parameter.
- /// The third type parameter.
- /// The fourth type parameter.
- /// The fifth type parameter.
- /// The return type parameter.
- /// The connection to the database. This can either be open or closed.
- /// The id of the entity in the database.
- /// The mapping to perform on the entities in the result set.
- /// The entity with the corresponding id joined with the specified types.
- public static TReturn Get(this IDbConnection connection,
- object id,
- Func map) where TReturn : class
- {
- return MultiMap(connection, map, id).FirstOrDefault();
- }
-
- ///
- /// Retrieves the entity of type with the specified id
- /// joined with the types specified as type parameters.
- ///
- /// The first type parameter. This is the source entity.
- /// The second type parameter.
- /// The third type parameter.
- /// The fourth type parameter.
- /// The fifth type parameter.
- /// The sixth type parameter.
- /// The return type parameter.
- /// The connection to the database. This can either be open or closed.
- /// The id of the entity in the database.
- /// The mapping to perform on the entities in the result set.
- /// The entity with the corresponding id joined with the specified types.
- public static TReturn Get(this IDbConnection connection,
- object id,
- Func map) where TReturn : class
- {
- return MultiMap(connection, map, id).FirstOrDefault();
- }
-
- ///
- /// Retrieves the entity of type with the specified id
- /// joined with the types specified as type parameters.
- ///
- /// The first type parameter. This is the source entity.
- /// The second type parameter.
- /// The third type parameter.
- /// The fourth type parameter.
- /// The fifth type parameter.
- /// The sixth type parameter.
- /// The seventh type parameter.
- /// The return type parameter.
- /// The connection to the database. This can either be open or closed.
- /// The id of the entity in the database.
- /// The mapping to perform on the entities in the result set.
- /// The entity with the corresponding id joined with the specified types.
- public static TReturn Get(this IDbConnection connection,
- object id,
- Func map) where TReturn : class
- {
- return MultiMap(connection, map, id).FirstOrDefault();
- }
-
- ///
- /// Retrieves all the entities of type .
- ///
- /// The type of the entity.
- /// The connection to the database. This can either be open or closed.
- ///
- /// A value indicating whether the result of the query should be executed directly,
- /// or when the query is materialized (using ToList() for example).
- ///
- /// A collection of entities of type .
- public static IEnumerable GetAll(this IDbConnection connection, bool buffered = true) where TEntity : class
- {
- var type = typeof (TEntity);
-
- string sql;
- if (!_getAllQueryCache.TryGetValue(type, out sql))
- {
- var tableName = Resolvers.Table(type);
- sql = string.Format("select * from {0}", tableName);
- _getAllQueryCache[type] = sql;
- }
-
- return connection.Query(sql: sql, buffered: buffered);
- }
-
- ///
- /// Retrieves all the entities of type
- /// joined with the types specified as type parameters.
- ///
- /// The first type parameter. This is the source entity.
- /// The second type parameter.
- /// The return type parameter.
- /// The connection to the database. This can either be open or closed.
- /// The mapping to perform on the entities in the result set.
- ///
- /// A value indicating whether the result of the query should be executed directly,
- /// or when the query is materialized (using ToList() for example).
- ///
- ///
- /// A collection of entities of type
- /// joined with the specified type types.
- ///
- public static IEnumerable GetAll(this IDbConnection connection, Func map, bool buffered = true)
- {
- return MultiMap(connection, map, buffered: buffered);
- }
-
- ///
- /// Retrieves all the entities of type
- /// joined with the types specified as type parameters.
- ///
- /// The first type parameter. This is the source entity.
- /// The second type parameter.
- /// The third type parameter.
- /// The return type parameter.
- /// The connection to the database. This can either be open or closed.
- /// The mapping to perform on the entities in the result set.
- ///
- /// A value indicating whether the result of the query should be executed directly,
- /// or when the query is materialized (using ToList() for example).
- ///
- ///
- /// A collection of entities of type
- /// joined with the specified type types.
- ///
- public static IEnumerable GetAll(this IDbConnection connection, Func map, bool buffered = true)
- {
- return MultiMap(connection, map, buffered: buffered);
- }
-
- ///
- /// Retrieves all the entities of type
- /// joined with the types specified as type parameters.
- ///
- /// The first type parameter. This is the source entity.
- /// The second type parameter.
- /// The third type parameter.
- /// The fourth type parameter.
- /// The return type parameter.
- /// The connection to the database. This can either be open or closed.
- /// The mapping to perform on the entities in the result set.
- ///
- /// A value indicating whether the result of the query should be executed directly,
- /// or when the query is materialized (using ToList() for example).
- ///
- ///
- /// A collection of entities of type
- /// joined with the specified type types.
- ///
- public static IEnumerable GetAll(this IDbConnection connection, Func map, bool buffered = true)
- {
- return MultiMap(connection, map, buffered: buffered);
- }
-
- ///
- /// Retrieves all the entities of type
- /// joined with the types specified as type parameters.
- ///
- /// The first type parameter. This is the source entity.
- /// The second type parameter.
- /// The third type parameter.
- /// The fourth type parameter.
- /// The fifth type parameter.
- /// The return type parameter.
- /// The connection to the database. This can either be open or closed.
- /// The mapping to perform on the entities in the result set.
- ///
- /// A value indicating whether the result of the query should be executed directly,
- /// or when the query is materialized (using ToList() for example).
- ///
- ///
- /// A collection of entities of type
- /// joined with the specified type types.
- ///
- public static IEnumerable GetAll(this IDbConnection connection, Func map, bool buffered = true)
- {
- return MultiMap(connection, map, buffered: buffered);
- }
-
- ///
- /// Retrieves all the entities of type
- /// joined with the types specified as type parameters.
- ///
- /// The first type parameter. This is the source entity.
- /// The second type parameter.
- /// The third type parameter.
- /// The fourth type parameter.
- /// The fifth type parameter.
- /// The sixth type parameter.
- /// The return type parameter.
- /// The connection to the database. This can either be open or closed.
- /// The mapping to perform on the entities in the result set.
- ///
- /// A value indicating whether the result of the query should be executed directly,
- /// or when the query is materialized (using ToList() for example).
- ///
- ///
- /// A collection of entities of type
- /// joined with the specified type types.
- ///
- public static IEnumerable GetAll(this IDbConnection connection, Func map, bool buffered = true)
- {
- return MultiMap(connection, map, buffered: buffered);
- }
-
- ///
- /// Retrieves all the entities of type
- /// joined with the types specified as type parameters.
- ///
- /// The first type parameter. This is the source entity.
- /// The second type parameter.
- /// The third type parameter.
- /// The fourth type parameter.
- /// The fifth type parameter.
- /// The sixth type parameter.
- /// The seventh type parameter.
- /// The return type parameter.
- /// The connection to the database. This can either be open or closed.
- /// The mapping to perform on the entities in the result set.
- ///
- /// A value indicating whether the result of the query should be executed directly,
- /// or when the query is materialized (using ToList() for example).
- ///
- ///
- /// A collection of entities of type
- /// joined with the specified type types.
- ///
- public static IEnumerable GetAll(this IDbConnection connection, Func map, bool buffered = true)
- {
- return MultiMap(connection, map, buffered: buffered);
- }
-
- private static IEnumerable MultiMap(IDbConnection connection, Delegate map, object id = null, bool buffered = true)
- {
- var resultType = typeof(TReturn);
- var resultTableName = Resolvers.Table(resultType);
- var resultTableKeyColumnName = Resolvers.Column(Resolvers.KeyProperty(resultType));
-
- var sql = string.Format("select * from {0}", resultTableName);
-
- var includeTypes = new[]
- {
- typeof (T1),
- typeof (T2),
- typeof (T3),
- typeof (T4),
- typeof (T5),
- typeof (T6),
- typeof (T7)
- }
- .Where(t => t != typeof (DontMap))
- .ToArray();
-
- for (var i = 1; i < includeTypes.Length; i++)
- {
- // Determine the table to join with.
- var sourceType = includeTypes[i - 1];
- var sourceTableName = Resolvers.Table(sourceType);
-
- // Determine the table name of the joined table.
- var includeType = includeTypes[i];
- var foreignKeyTableName = Resolvers.Table(includeType);
-
- // Determine the foreign key and the relationship type.
- ForeignKeyRelation relation;
- var foreignKeyProperty = Resolvers.ForeignKeyProperty(sourceType, includeType, out relation);
- var foreignKeyPropertyName = Resolvers.Column(foreignKeyProperty);
-
- // If the foreign key property is nullable, use a left-join.
- var joinType = Nullable.GetUnderlyingType(foreignKeyProperty.PropertyType) != null
- ? "left"
- : "inner";
-
- switch (relation)
- {
- case ForeignKeyRelation.OneToOne:
- // Determine the primary key of the foreign key table.
- var foreignKeyTableKeyColumName = Resolvers.Column(Resolvers.KeyProperty(includeType));
-
- sql += string.Format(" {0} join {1} on {2}.{3} = {1}.{4}",
- joinType,
- foreignKeyTableName,
- sourceTableName,
- foreignKeyPropertyName,
- foreignKeyTableKeyColumName);
- break;
-
- case ForeignKeyRelation.OneToMany:
- // Determine the primary key of the source table.
- var sourceKeyColumnName = Resolvers.Column(Resolvers.KeyProperty(sourceType));
-
- sql += string.Format(" {0} join {1} on {2}.{3} = {1}.{4}",
- joinType,
- foreignKeyTableName,
- sourceTableName,
- sourceKeyColumnName,
- foreignKeyPropertyName);
- break;
-
- case ForeignKeyRelation.ManyToMany:
- throw new NotImplementedException("Many-to-many relationships are not supported yet.");
-
- default:
- throw new NotImplementedException(string.Format("Foreign key relation type '{0}' is not implemented.", relation));
- }
- }
-
- DynamicParameters parameters = null;
- if (id != null)
- {
- sql += string.Format(" where {0}.{1} = @{1}", resultTableName, resultTableKeyColumnName);
-
- parameters = new DynamicParameters();
- parameters.Add("Id", id);
- }
-
- switch (includeTypes.Length)
- {
- case 2:
- return connection.Query(sql, (Func)map, parameters, buffered: buffered);
- case 3:
- return connection.Query(sql, (Func)map, parameters, buffered: buffered);
- case 4:
- return connection.Query(sql, (Func)map, parameters, buffered: buffered);
- case 5:
- return connection.Query(sql, (Func)map, parameters, buffered: buffered);
- case 6:
- return connection.Query(sql, (Func)map, parameters, buffered: buffered);
- case 7:
- return connection.Query(sql, (Func)map, parameters, buffered: buffered);
- }
-
- throw new InvalidOperationException();
- }
-
- private class DontMap
- {
- }
-
- ///
- /// Selects all the entities matching the specified predicate.
- ///
- /// The type of the entity.
- /// The connection to the database. This can either be open or closed.
- /// A predicate to filter the results.
- ///
- /// A value indicating whether the result of the query should be executed directly,
- /// or when the query is materialized (using ToList() for example).
- ///
- /// A collection of entities of type matching the specified .
- public static IEnumerable Select(this IDbConnection connection, Expression> predicate, bool buffered = true)
- {
- var type = typeof (TEntity);
-
- string sql;
- if (!_getAllQueryCache.TryGetValue(type, out sql))
- {
- var tableName = Resolvers.Table(type);
- sql = string.Format("select * from {0}", tableName);
- _getAllQueryCache[type] = sql;
- }
-
- DynamicParameters parameters;
- sql += new SqlExpression()
- .Where(predicate)
- .ToSql(out parameters);
-
- return connection.Query(sql: sql, param: parameters, buffered: buffered);
- }
-
- ///
- /// Represents a typed SQL expression.
- ///
- /// The type of the entity.
- public class SqlExpression
- {
- private readonly StringBuilder _whereBuilder = new StringBuilder();
- private readonly DynamicParameters _parameters = new DynamicParameters();
- private int _parameterIndex;
-
- ///
- /// Builds a SQL expression for the specified filter expression.
- ///
- /// The filter expression on the entity.
- /// The current instance.
- public virtual SqlExpression Where(Expression> expression)
- {
- AppendToWhere("and", expression);
- return this;
- }
-
- private void AppendToWhere(string conditionOperator, Expression expression)
- {
- var sqlExpression = VisitExpression(expression).ToString();
- AppendToWhere(conditionOperator, sqlExpression);
- }
-
- private void AppendToWhere(string conditionOperator, string sqlExpression)
- {
- if (_whereBuilder.Length == 0)
- {
- _whereBuilder.Append(" where ");
- }
- else
- {
- _whereBuilder.AppendFormat(" {0} ", conditionOperator);
- }
-
- _whereBuilder.Append(sqlExpression);
- }
-
- ///
- /// Visits the expression.
- ///
- /// The expression to visit.
- /// The result of the visit.
- protected virtual object VisitExpression(Expression expression)
- {
- switch (expression.NodeType)
- {
- case ExpressionType.Lambda:
- return VisitLambda(expression as LambdaExpression);
-
- case ExpressionType.LessThan:
- case ExpressionType.LessThanOrEqual:
- case ExpressionType.GreaterThan:
- case ExpressionType.GreaterThanOrEqual:
- case ExpressionType.Equal:
- case ExpressionType.NotEqual:
- case ExpressionType.And:
- case ExpressionType.AndAlso:
- case ExpressionType.Or:
- case ExpressionType.OrElse:
- return VisitBinary((BinaryExpression)expression);
-
- case ExpressionType.Convert:
- case ExpressionType.Not:
- return VisitUnary((UnaryExpression)expression);
-
- case ExpressionType.New:
- return VisitNew((NewExpression)expression);
-
- case ExpressionType.MemberAccess:
- return VisitMemberAccess((MemberExpression)expression);
-
- case ExpressionType.Constant:
- return VisitConstantExpression((ConstantExpression)expression);
- }
-
- return expression;
- }
-
- ///
- /// Processes a lambda expression.
- ///
- /// The lambda expression.
- /// The result of the processing.
- protected virtual object VisitLambda(LambdaExpression epxression)
- {
- if (epxression.Body.NodeType == ExpressionType.MemberAccess)
- {
- var member = epxression.Body as MemberExpression;
- if (member.Expression != null)
- {
- return string.Format("{0} = '1'", VisitMemberAccess(member));
- }
- }
-
- return VisitExpression(epxression.Body);
- }
-
- ///
- /// Processes a binary expression.
- ///
- /// The binary expression.
- /// The result of the processing.
- protected virtual object VisitBinary(BinaryExpression expression)
- {
- object left, right;
- var operand = BindOperant(expression.NodeType);
- if (operand == "and" || operand == "or")
- {
- // Left side.
- var member = expression.Left as MemberExpression;
- if (member != null &&
- member.Expression != null &&
- member.Expression.NodeType == ExpressionType.Parameter)
- {
- left = string.Format("{0} = '1'", VisitMemberAccess(member));
- }
- else
- {
- left = VisitExpression(expression.Left);
- }
-
- // Right side.
- member = expression.Right as MemberExpression;
- if (member != null &&
- member.Expression != null &&
- member.Expression.NodeType == ExpressionType.Parameter)
- {
- right = string.Format("{0} = '1'", VisitMemberAccess(member));
- }
- else
- {
- right = VisitExpression(expression.Right);
- }
- }
- else
- {
- // It's a single expression.
- left = VisitExpression(expression.Left);
- right = VisitExpression(expression.Right);
-
- var paramName = "p" + _parameterIndex++;
- _parameters.Add(paramName, value: right);
- return string.Format("{0} {1} @{2}", left, operand, paramName);
- }
-
- return string.Format("{0} {1} {2}", left, operand, right);
- }
-
- ///
- /// Processes a unary expression.
- ///
- /// The unary expression.
- /// The result of the processing.
- protected virtual object VisitUnary(UnaryExpression expression)
- {
- switch (expression.NodeType)
- {
- case ExpressionType.Not:
- var o = VisitExpression(expression.Operand);
- if (!(o is string))
- {
- return !(bool)o;
- }
-
- var memberExpression = expression.Operand as MemberExpression;
- if (memberExpression != null &&
- Resolvers.Properties(memberExpression.Expression.Type).Any(p => p.Name == (string)o))
- {
- o = string.Format("{0} = '1'", o);
- }
-
- return string.Format("not ({0})", o);
- case ExpressionType.Convert:
- if (expression.Method != null)
- {
- return Expression.Lambda(expression).Compile().DynamicInvoke();
- }
- break;
- }
-
- return VisitExpression(expression.Operand);
- }
-
- ///
- /// Processes a new expression.
- ///
- /// The new expression.
- /// The result of the processing.
- protected virtual object VisitNew(NewExpression expression)
- {
- var member = Expression.Convert(expression, typeof (object));
- var lambda = Expression.Lambda>(member);
- var getter = lambda.Compile();
- return getter();
- }
-
- ///
- /// Processes a member access expression.
- ///
- /// The member access expression.
- /// The result of the processing.
- protected virtual object VisitMemberAccess(MemberExpression expression)
- {
- if (expression.Expression != null && expression.Expression.NodeType == ExpressionType.Parameter)
- {
- return MemberToColumn(expression);
- }
-
- var member = Expression.Convert(expression, typeof (object));
- var lambda = Expression.Lambda>(member);
- var getter = lambda.Compile();
- return getter();
- }
-
- ///
- /// Processes a constant expression.
- ///
- /// The constant expression.
- /// The result of the processing.
- protected virtual object VisitConstantExpression(ConstantExpression expression)
- {
- return expression.Value ?? "null";
- }
-
- ///
- /// Proccesses a member expression.
- ///
- /// The member expression.
- /// The result of the processing.
- protected virtual string MemberToColumn(MemberExpression expression)
- {
- return Resolvers.Column((PropertyInfo)expression.Member);
- }
-
- ///
- /// Returns the expression operand for the specified expression type.
- ///
- /// The expression type for node of an expression tree.
- /// The expression operand equivalent of the .
- protected virtual string BindOperant(ExpressionType expressionType)
- {
- switch (expressionType)
- {
- case ExpressionType.Equal:
- return "=";
- case ExpressionType.NotEqual:
- return "<>";
- case ExpressionType.GreaterThan:
- return ">";
- case ExpressionType.GreaterThanOrEqual:
- return ">=";
- case ExpressionType.LessThan:
- return "<";
- case ExpressionType.LessThanOrEqual:
- return "<=";
- case ExpressionType.AndAlso:
- return "and";
- case ExpressionType.OrElse:
- return "or";
- case ExpressionType.Add:
- return "+";
- case ExpressionType.Subtract:
- return "-";
- case ExpressionType.Multiply:
- return "*";
- case ExpressionType.Divide:
- return "/";
- case ExpressionType.Modulo:
- return "MOD";
- case ExpressionType.Coalesce:
- return "COALESCE";
- default:
- return expressionType.ToString();
- }
- }
-
- ///
- /// Returns the current SQL query.
- ///
- /// The current SQL query.
- public string ToSql()
- {
- return _whereBuilder.ToString();
- }
-
- ///
- /// Returns the current SQL query.
- ///
- /// When this method returns, contains the parameters for the query.
- /// The current SQL query.
- public string ToSql(out DynamicParameters parameters)
- {
- parameters = _parameters;
- return _whereBuilder.ToString();
- }
-
- ///
- /// Returns the current SQL query.
- ///
- /// The current SQL query.
- public override string ToString()
- {
- return _whereBuilder.ToString();
- }
- }
-
- ///
- /// Inserts the specified entity into the database and returns the id.
- ///
- /// The type of the entity.
- /// The connection to the database. This can either be open or closed.
- /// The entity to be inserted.
- /// Optional transaction for the command.
- /// The id of the inserted entity.
- public static int Insert(this IDbConnection connection, TEntity entity, IDbTransaction transaction = null) where TEntity : class
- {
- var type = typeof (TEntity);
-
- string sql;
- if (!_insertQueryCache.TryGetValue(type, out sql))
- {
- var tableName = Resolvers.Table(type);
- var keyProperty = Resolvers.KeyProperty(type);
- var typeProperties = Resolvers.Properties(type).Where(p => p != keyProperty).ToList();
-
- var columnNames = typeProperties.Select(Resolvers.Column).ToArray();
- var paramNames = typeProperties.Select(p => "@" + p.Name).ToArray();
-
- var builder = GetBuilder(connection);
-
- sql = builder.BuildInsert(tableName, columnNames, paramNames, keyProperty);
-
- _insertQueryCache[type] = sql;
- }
-
- var result = connection.Query(sql, entity, transaction);
- return result.Single();
- }
-
- ///
- /// Updates the values of the specified entity in the database.
- /// The return value indicates whether the operation succeeded.
- ///
- /// The type of the entity.
- /// The connection to the database. This can either be open or closed.
- /// The entity in the database.
- /// Optional transaction for the command.
- /// A value indicating whether the update operation succeeded.
- public static bool Update(this IDbConnection connection, TEntity entity, IDbTransaction transaction = null)
- {
- var type = typeof (TEntity);
-
- string sql;
- if (!_updateQueryCache.TryGetValue(type, out sql))
- {
- var tableName = Resolvers.Table(type);
- var keyProperty = Resolvers.KeyProperty(type);
- var typeProperties = Resolvers.Properties(type).Where(p => p != keyProperty).ToList();
-
- var columnNames = typeProperties.Select(p => string.Format("{0} = @{1}", Resolvers.Column(p), p.Name)).ToArray();
-
- sql = string.Format("update {0} set {1} where {2} = @{3}",
- tableName,
- string.Join(", ", columnNames),
- Resolvers.Column(keyProperty),
- keyProperty.Name);
-
- _updateQueryCache[type] = sql;
- }
-
- return connection.Execute(sql, entity, transaction) > 0;
- }
-
- ///
- /// Deletes the specified entity from the database.
- /// Returns a value indicating whether the operation succeeded.
- ///
- /// The type of the entity.
- /// The connection to the database. This can either be open or closed.
- /// The entity to be deleted.
- /// Optional transaction for the command.
- /// A value indicating whether the delete operation succeeded.
- public static bool Delete(this IDbConnection connection, TEntity entity, IDbTransaction transaction = null)
- {
- var type = typeof (TEntity);
-
- string sql;
- if (!_deleteQueryCache.TryGetValue(type, out sql))
- {
- var tableName = Resolvers.Table(type);
- var keyProperty = Resolvers.KeyProperty(type);
- var keyColumnName = Resolvers.Column(keyProperty);
-
- sql = string.Format("delete from {0} where {1} = @{2}", tableName, keyColumnName, keyProperty.Name);
-
- _deleteQueryCache[type] = sql;
- }
-
- return connection.Execute(sql, entity, transaction) > 0;
- }
-
- ///
- /// Helper class for retrieving type metadata to build sql queries using configured resolvers.
- ///
- public static class Resolvers
- {
- private static readonly IDictionary _typeTableNameCache = new Dictionary();
- private static readonly IDictionary _columnNameCache = new Dictionary();
- private static readonly IDictionary _typeKeyPropertyCache = new Dictionary();
- private static readonly IDictionary _typePropertiesCache = new Dictionary();
- private static readonly IDictionary _typeForeignKeyPropertyCache = new Dictionary();
-
- ///
- /// Gets the key property for the specified type, using the configured .
- ///
- /// The to get the key property for.
- /// The key property for .
- public static PropertyInfo KeyProperty(Type type)
- {
- PropertyInfo keyProperty;
- if (!_typeKeyPropertyCache.TryGetValue(type, out keyProperty))
- {
- keyProperty = _keyPropertyResolver.ResolveKeyProperty(type);
- _typeKeyPropertyCache[type] = keyProperty;
- }
-
- return keyProperty;
- }
-
- ///
- /// Gets the foreign key property for the specified source type and including type
- /// using the configure d.
- ///
- /// The source type which should contain the foreign key property.
- /// The type of the foreign key relation.
- /// The foreign key relationship type.
- /// The foreign key property for and .
- public static PropertyInfo ForeignKeyProperty(Type sourceType, Type includingType, out ForeignKeyRelation foreignKeyRelation)
- {
- var key = string.Format("{0};{1}", sourceType.FullName, includingType.FullName);
-
- ForeignKeyInfo foreignKeyInfo;
- if (!_typeForeignKeyPropertyCache.TryGetValue(key, out foreignKeyInfo))
- {
- // Resole the property and relation.
- var foreignKeyProperty = _foreignKeyPropertyResolver.ResolveForeignKeyProperty(sourceType, includingType, out foreignKeyRelation);
-
- // Cache the info.
- foreignKeyInfo = new ForeignKeyInfo(foreignKeyProperty, foreignKeyRelation);
- _typeForeignKeyPropertyCache[key] = foreignKeyInfo;
- }
-
- foreignKeyRelation = foreignKeyInfo.Relation;
- return foreignKeyInfo.PropertyInfo;
- }
-
- private class ForeignKeyInfo
- {
- public ForeignKeyInfo(PropertyInfo propertyInfo, ForeignKeyRelation relation)
- {
- PropertyInfo = propertyInfo;
- Relation = relation;
- }
-
- public PropertyInfo PropertyInfo { get; private set; }
-
- public ForeignKeyRelation Relation { get; private set; }
- }
-
- ///
- /// Gets the properties to be mapped for the specified type, using the configured .
- ///
- /// The to get the properties from.
- /// >The collection of to be mapped properties of .
- public static IEnumerable Properties(Type type)
- {
- PropertyInfo[] properties;
- if (!_typePropertiesCache.TryGetValue(type, out properties))
- {
- properties = _propertyResolver.ResolveProperties(type).ToArray();
- _typePropertiesCache[type] = properties;
- }
-
- return properties;
- }
-
- ///
- /// Gets the name of the table in the database for the specified type,
- /// using the configured .
- ///
- /// The to get the table name for.
- /// The table name in the database for .
- public static string Table(Type type)
- {
- string name;
- if (!_typeTableNameCache.TryGetValue(type, out name))
- {
- name = _tableNameResolver.ResolveTableName(type);
- _typeTableNameCache[type] = name;
- }
- return name;
- }
-
- ///
- /// Gets the name of the column in the database for the specified type,
- /// using the configured .
- ///
- /// The to get the column name for.
- /// The column name in the database for .
- public static string Column(PropertyInfo propertyInfo)
- {
- var key = string.Format("{0}.{1}", propertyInfo.DeclaringType, propertyInfo.Name);
-
- string columnName;
- if (!_columnNameCache.TryGetValue(key, out columnName))
- {
- columnName = _columnNameResolver.ResolveColumnName(propertyInfo);
- _columnNameCache[key] = columnName;
- }
-
- return columnName;
- }
-
- ///
- /// Provides access to default resolver implementations.
- ///
- public static class Default
- {
- ///
- /// The default column name resolver.
- ///
- public static readonly IColumnNameResolver ColumnNameResolver = new DefaultColumnNameResolver();
-
- ///
- /// The default property resolver.
- ///
- public static readonly IPropertyResolver PropertyResolver = new DefaultPropertyResolver();
-
- ///
- /// The default key property resolver.
- ///
- public static readonly IKeyPropertyResolver KeyPropertyResolver = new DefaultKeyPropertyResolver();
-
- ///
- /// The default table name resolver.
- ///
- public static readonly ITableNameResolver TableNameResolver = new DefaultTableNameResolver();
- }
- }
-
- #region Property resolving
- private static IPropertyResolver _propertyResolver = new DefaultPropertyResolver();
-
- ///
- /// Defines methods for resolving the properties of entities.
- /// Custom implementations can be registerd with .
- ///
- public interface IPropertyResolver
- {
- ///
- /// Resolves the properties to be mapped for the specified type.
- ///
- /// The type to resolve the properties to be mapped for.
- /// A collection of 's of the .
- IEnumerable ResolveProperties(Type type);
- }
-
- ///
- /// Sets the implementation for resolving key of entities.
- ///
- /// An instance of .
- public static void SetPropertyResolver(IPropertyResolver propertyResolver)
- {
- _propertyResolver = propertyResolver;
- }
-
- ///
- /// Represents the base class for property resolvers.
- ///
- public abstract class PropertyResolverBase : IPropertyResolver
- {
- private static readonly HashSet _primitiveTypes = new HashSet
- {
- typeof (object),
- typeof (string),
- typeof(Guid),
- typeof (decimal),
- typeof (double),
- typeof (float),
- typeof (DateTime),
- typeof (TimeSpan)
- };
-
- ///
- /// Resolves the properties to be mapped for the specified type.
- ///
- /// The type to resolve the properties to be mapped for.
- /// A collection of 's of the .
- public abstract IEnumerable ResolveProperties(Type type);
-
- ///
- /// Gets a collection of types that are considered 'primitive' for Dommel but are not for the CLR.
- /// Override this if you need your own implementation of this.
- ///
- protected virtual HashSet PrimitiveTypes
- {
- get
- {
- return _primitiveTypes;
- }
- }
-
- ///
- /// Filters the complex types from the specified collection of properties.
- ///
- /// A collection of properties.
- /// The properties that are considered 'primitive' of .
- protected virtual IEnumerable FilterComplexTypes(IEnumerable properties)
- {
- foreach (var property in properties)
- {
- var type = property.PropertyType;
- type = Nullable.GetUnderlyingType(type) ?? type;
-
-#if DNXCORE50
- if (type.GetTypeInfo().IsPrimitive || type.GetTypeInfo().IsEnum || PrimitiveTypes.Contains(type))
-#else
- if (type.IsPrimitive || type.IsEnum || PrimitiveTypes.Contains(type))
-#endif
- {
- yield return property;
- }
- }
- }
- }
-
- ///
- /// Default implemenation of the interface.
- ///
- public class DefaultPropertyResolver : PropertyResolverBase
- {
- public override IEnumerable ResolveProperties(Type type)
- {
- return FilterComplexTypes(type.GetProperties());
- }
- }
- #endregion
-
- #region Key property resolving
- private static IKeyPropertyResolver _keyPropertyResolver = new DefaultKeyPropertyResolver();
-
- ///
- /// Sets the implementation for resolving key properties of entities.
- ///
- /// An instance of .
- public static void SetKeyPropertyResolver(IKeyPropertyResolver resolver)
- {
- _keyPropertyResolver = resolver;
- }
-
- ///
- /// Defines methods for resolving the key property of entities.
- /// Custom implementations can be registerd with .
- ///
- public interface IKeyPropertyResolver
- {
- ///
- /// Resolves the key property for the specified type.
- ///
- /// The type to resolve the key property for.
- /// A instance of the key property of .
- PropertyInfo ResolveKeyProperty(Type type);
- }
-
- ///
- /// Implements the interface by resolving key properties
- /// with the [Key] attribute or with the name 'Id'.
- ///
- public class DefaultKeyPropertyResolver : IKeyPropertyResolver
- {
- ///
- /// Finds the key property by looking for a property with the [Key] attribute or with the name 'Id'.
- ///
- public virtual PropertyInfo ResolveKeyProperty(Type type)
- {
- var allProps = Resolvers.Properties(type).ToList();
-
- // Look for properties with the [Key] attribute.
- var keyProps = allProps.Where(p => p.GetCustomAttributes(true).Any(a => a is KeyAttribute)).ToList();
-
- if (keyProps.Count == 0)
- {
- // Search for properties named as 'Id' as fallback.
- keyProps = allProps.Where(p => p.Name.Equals("Id", StringComparison.OrdinalIgnoreCase)).ToList();
- }
-
- if (keyProps.Count == 0)
- {
- throw new Exception(string.Format("Could not find the key property for type '{0}'.", type.FullName));
- }
-
- if (keyProps.Count > 1)
- {
- throw new Exception(string.Format("Multiple key properties were found for type '{0}'.", type.FullName));
- }
-
- return keyProps[0];
- }
- }
- #endregion
-
- #region Foreign key property resolving
- ///
- /// Describes a foreign key relationship.
- ///
- public enum ForeignKeyRelation
- {
- ///
- /// Specifies a one-to-one relationship.
- ///
- OneToOne,
-
- ///
- /// Specifies a one-to-many relationship.
- ///
- OneToMany,
-
- ///
- /// Specifies a many-to-many relationship.
- ///
- ManyToMany
- }
-
- private static IForeignKeyPropertyResolver _foreignKeyPropertyResolver = new DefaultForeignKeyPropertyResolver();
-
- ///
- /// Sets the implementation for resolving foreign key properties.
- ///
- /// An instance of .
- public static void SetForeignKeyPropertyResolver(IForeignKeyPropertyResolver resolver)
- {
- _foreignKeyPropertyResolver = resolver;
- }
-
- ///
- /// Defines methods for resolving foreign key properties.
- ///
- public interface IForeignKeyPropertyResolver
- {
- ///
- /// Resolves the foreign key property for the specified source type and including type.
- ///
- /// The source type which should contain the foreign key property.
- /// The type of the foreign key relation.
- /// The foreign key relationship type.
- /// The foreign key property for and .
- PropertyInfo ResolveForeignKeyProperty(Type sourceType, Type includingType, out ForeignKeyRelation foreignKeyRelation);
- }
-
- ///
- /// Implements the interface.
- ///
- public class DefaultForeignKeyPropertyResolver : IForeignKeyPropertyResolver
- {
- ///
- /// Resolves the foreign key property for the specified source type and including type
- /// by using + Id as property name.
- ///
- /// The source type which should contain the foreign key property.
- /// The type of the foreign key relation.
- /// The foreign key relationship type.
- /// The foreign key property for and .
- public virtual PropertyInfo ResolveForeignKeyProperty(Type sourceType, Type includingType, out ForeignKeyRelation foreignKeyRelation)
- {
- var oneToOneFk = ResolveOneToOne(sourceType, includingType);
- if (oneToOneFk != null)
- {
- foreignKeyRelation = ForeignKeyRelation.OneToOne;
- return oneToOneFk;
- }
-
- var oneToManyFk = ResolveOneToMany(sourceType, includingType);
- if (oneToManyFk != null)
- {
- foreignKeyRelation = ForeignKeyRelation.OneToMany;
- return oneToManyFk;
- }
-
- var msg = string.Format("Could not resolve foreign key property. Source type '{0}'; including type: '{1}'.", sourceType.FullName, includingType.FullName);
- throw new Exception(msg);
- }
-
- private static PropertyInfo ResolveOneToOne(Type sourceType, Type includingType)
- {
- // Look for the foreign key on the source type.
- var foreignKeyName = includingType.Name + "Id";
- var foreignKeyProperty = sourceType.GetProperties().FirstOrDefault(p => p.Name == foreignKeyName);
-
- return foreignKeyProperty;
- }
-
- private static PropertyInfo ResolveOneToMany(Type sourceType, Type includingType)
- {
- // Look for the foreign key on the including type.
- var foreignKeyName = sourceType.Name + "Id";
- var foreignKeyProperty = includingType.GetProperties().FirstOrDefault(p => p.Name == foreignKeyName);
- return foreignKeyProperty;
- }
- }
- #endregion
-
- #region Table name resolving
- private static ITableNameResolver _tableNameResolver = new DefaultTableNameResolver();
-
- ///
- /// Sets the implementation for resolving table names for entities.
- ///
- /// An instance of .
- public static void SetTableNameResolver(ITableNameResolver resolver)
- {
- _tableNameResolver = resolver;
- }
-
- ///
- /// Defines methods for resolving table names of entities.
- /// Custom implementations can be registerd with .
- ///
- public interface ITableNameResolver
- {
- ///
- /// Resolves the table name for the specified type.
- ///
- /// The type to resolve the table name for.
- /// A string containing the resolved table name for for .
- string ResolveTableName(Type type);
- }
-
- ///
- /// Implements the interface by resolving table names
- /// by making the type name plural and removing the 'I' prefix for interfaces.
- ///
- public class DefaultTableNameResolver : ITableNameResolver
- {
- ///
- /// Resolves the table name by making the type plural (+ 's', Product -> Products)
- /// and removing the 'I' prefix for interfaces.
- ///
- public virtual string ResolveTableName(Type type)
- {
- var name = type.Name + "s";
-#if DNXCORE50
- if (type.GetTypeInfo().IsInterface && name.StartsWith("I"))
-#else
- if (type.IsInterface && name.StartsWith("I"))
-#endif
- {
- name = name.Substring(1);
- }
-
- // todo: add [Table] attribute support.
- return name;
- }
- }
- #endregion
-
- #region Column name resolving
- private static IColumnNameResolver _columnNameResolver = new DefaultColumnNameResolver();
-
- ///
- /// Sets the implementation for resolving column names.
- ///
- /// An instance of .
- public static void SetColumnNameResolver(IColumnNameResolver resolver)
- {
- _columnNameResolver = resolver;
- }
-
- ///
- /// Defines methods for resolving column names for entities.
- /// Custom implementations can be registerd with .
- ///
- public interface IColumnNameResolver
- {
- ///
- /// Resolves the column name for the specified property.
- ///
- /// The property of the entity.
- /// The column name for the property.
- string ResolveColumnName(PropertyInfo propertyInfo);
- }
-
- ///
- /// Implements the .
- ///
- public class DefaultColumnNameResolver : IColumnNameResolver
- {
- ///
- /// Resolves the column name for the property. This is just the name of the property.
- ///
- public virtual string ResolveColumnName(PropertyInfo propertyInfo)
- {
- return propertyInfo.Name;
- }
- }
- #endregion
-
- #region Sql builders
- ///
- /// Adds a custom implementation of
- /// for the specified ADO.NET connection type.
- ///
- ///
- /// The ADO.NET conncetion type to use the with.
- /// Example: typeof(SqlConnection).
- ///
- /// An implementation of the .
- public static void AddSqlBuilder(Type connectionType, ISqlBuilder builder)
- {
- _sqlBuilders[connectionType.Name.ToLower()] = builder;
- }
-
- private static ISqlBuilder GetBuilder(IDbConnection connection)
- {
- var connectionName = connection.GetType().Name.ToLower();
- ISqlBuilder builder;
- return _sqlBuilders.TryGetValue(connectionName, out builder) ? builder : new SqlServerSqlBuilder();
- }
-
- ///
- /// Defines methods for building specialized SQL queries.
- ///
- public interface ISqlBuilder
- {
- ///
- /// Builds an insert query using the specified table name, column names and parameter names.
- /// A query to fetch the new id will be included as well.
- ///
- /// The name of the table to query.
- /// The names of the columns in the table.
- /// The names of the parameters in the database command.
- /// The key property. This can be used to query a specific column for the new id. This is optional.
- /// An insert query including a query to fetch the new id.
- string BuildInsert(string tableName, string[] columnNames, string[] paramNames, PropertyInfo keyProperty);
- }
-
- private sealed class SqlServerSqlBuilder : ISqlBuilder
- {
- public string BuildInsert(string tableName, string[] columnNames, string[] paramNames, PropertyInfo keyProperty)
- {
- return string.Format("set nocount on insert into {0} ({1}) values ({2}) select cast(scope_identity() as int)",
- tableName,
- string.Join(", ", columnNames),
- string.Join(", ", paramNames));
- }
- }
-
- private sealed class SqlServerCeSqlBuilder : ISqlBuilder
- {
- public string BuildInsert(string tableName, string[] columnNames, string[] paramNames, PropertyInfo keyProperty)
- {
- return string.Format("insert into {0} ({1}) values ({2}) select cast(@@IDENTITY as int)",
- tableName,
- string.Join(", ", columnNames),
- string.Join(", ", paramNames));
- }
- }
-
- private sealed class SqliteSqlBuilder : ISqlBuilder
- {
- public string BuildInsert(string tableName, string[] columnNames, string[] paramNames, PropertyInfo keyProperty)
- {
- return string.Format("insert into {0} ({1}) values ({2}); select last_insert_rowid() id",
- tableName,
- string.Join(", ", columnNames),
- string.Join(", ", paramNames));
- }
- }
-
- private sealed class MySqlSqlBuilder : ISqlBuilder
- {
- public string BuildInsert(string tableName, string[] columnNames, string[] paramNames, PropertyInfo keyProperty)
- {
- return string.Format("insert into {0} ({1}) values ({2}) select LAST_INSERT_ID() id",
- tableName,
- string.Join(", ", columnNames),
- string.Join(", ", paramNames));
- }
- }
-
- private sealed class PostgresSqlBuilder : ISqlBuilder
- {
- public string BuildInsert(string tableName, string[] columnNames, string[] paramNames, PropertyInfo keyProperty)
- {
- var sql = string.Format("insert into {0} ({1}) values ({2}) select last_insert_rowid() id",
- tableName,
- string.Join(", ", columnNames),
- string.Join(", ", paramNames));
-
- if (keyProperty != null)
- {
- var keyColumnName = Resolvers.Column(keyProperty);
-
- sql += " RETURNING " + keyColumnName;
- }
- else
- {
- // todo: what behavior is desired here?
- throw new Exception("A key property is required for the PostgresSqlBuilder.");
- }
-
- return sql;
- }
- }
- #endregion
- }
-}
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Data;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using System.Text;
+using Dapper;
+
+#if DOTNET5_4
+using IDbTransaction = System.Data.Common.DbTransaction;
+using IDbConnection = System.Data.Common.DbConnection;
+#endif
+
+namespace Dommel
+{
+ ///
+ /// Simple CRUD operations for Dapper.
+ ///
+ public static class DommelMapper
+ {
+ private static readonly IDictionary _sqlBuilders = new Dictionary
+ {
+ { "sqlconnection", new SqlServerSqlBuilder() },
+ { "sqlceconnection", new SqlServerCeSqlBuilder() },
+ { "sqliteconnection", new SqliteSqlBuilder() },
+ { "npgsqlconnection", new PostgresSqlBuilder() },
+ { "mysqlconnection", new MySqlSqlBuilder() }
+ };
+
+ private static readonly IDictionary _getQueryCache = new Dictionary();
+ private static readonly IDictionary _getAllQueryCache = new Dictionary();
+ private static readonly IDictionary _insertQueryCache = new Dictionary();
+ private static readonly IDictionary _updateQueryCache = new Dictionary();
+ private static readonly IDictionary _deleteQueryCache = new Dictionary();
+
+ ///
+ /// Retrieves the entity of type with the specified id.
+ ///
+ /// The type of the entity.
+ /// The connection to the database. This can either be open or closed.
+ /// The id of the entity in the database.
+ /// The entity with the corresponding id.
+ public static TEntity Get(this IDbConnection connection, object id) where TEntity : class
+ {
+ var type = typeof (TEntity);
+
+ string sql;
+ if (!_getQueryCache.TryGetValue(type, out sql))
+ {
+ var tableName = Resolvers.Table(type);
+ var keyProperty = Resolvers.KeyProperty(type);
+ var keyColumnName = Resolvers.Column(keyProperty);
+
+ sql = string.Format("select * from {0} where {1} = @Id", tableName, keyColumnName);
+ _getQueryCache[type] = sql;
+ }
+
+ var parameters = new DynamicParameters();
+ parameters.Add("Id", id);
+
+ return connection.Query(sql: sql, param: parameters).FirstOrDefault();
+ }
+
+ ///
+ /// Retrieves the entity of type with the specified id
+ /// joined with the types specified as type parameters.
+ ///
+ /// The first type parameter. This is the source entity.
+ /// The second type parameter.
+ /// The return type parameter.
+ /// The connection to the database. This can either be open or closed.
+ /// The id of the entity in the database.
+ /// The mapping to perform on the entities in the result set.
+ /// The entity with the corresponding id joined with the specified types.
+ public static TReturn Get(this IDbConnection connection, object id, Func map) where TReturn : class
+ {
+ return MultiMap(connection, map, id).FirstOrDefault();
+ }
+
+ ///
+ /// Retrieves the entity of type with the specified id
+ /// joined with the types specified as type parameters.
+ ///
+ /// The first type parameter. This is the source entity.
+ /// The second type parameter.
+ /// The third type parameter.
+ /// The return type parameter.
+ /// The connection to the database. This can either be open or closed.
+ /// The id of the entity in the database.
+ /// The mapping to perform on the entities in the result set.
+ /// The entity with the corresponding id joined with the specified types.
+ public static TReturn Get(this IDbConnection connection,
+ object id,
+ Func map) where TReturn : class
+ {
+ return MultiMap(connection, map, id).FirstOrDefault();
+ }
+
+ ///
+ /// Retrieves the entity of type with the specified id
+ /// joined with the types specified as type parameters.
+ ///
+ /// The first type parameter. This is the source entity.
+ /// The second type parameter.
+ /// The third type parameter.
+ /// The fourth type parameter.
+ /// The return type parameter.
+ /// The connection to the database. This can either be open or closed.
+ /// The id of the entity in the database.
+ /// The mapping to perform on the entities in the result set.
+ /// The entity with the corresponding id joined with the specified types.
+ public static TReturn Get(this IDbConnection connection,
+ object id,
+ Func map) where TReturn : class
+ {
+ return MultiMap(connection, map, id).FirstOrDefault();
+ }
+
+ ///
+ /// Retrieves the entity of type with the specified id
+ /// joined with the types specified as type parameters.
+ ///
+ /// The first type parameter. This is the source entity.
+ /// The second type parameter.
+ /// The third type parameter.
+ /// The fourth type parameter.
+ /// The fifth type parameter.
+ /// The return type parameter.
+ /// The connection to the database. This can either be open or closed.
+ /// The id of the entity in the database.
+ /// The mapping to perform on the entities in the result set.
+ /// The entity with the corresponding id joined with the specified types.
+ public static TReturn Get(this IDbConnection connection,
+ object id,
+ Func map) where TReturn : class
+ {
+ return MultiMap(connection, map, id).FirstOrDefault();
+ }
+
+ ///
+ /// Retrieves the entity of type with the specified id
+ /// joined with the types specified as type parameters.
+ ///
+ /// The first type parameter. This is the source entity.
+ /// The second type parameter.
+ /// The third type parameter.
+ /// The fourth type parameter.
+ /// The fifth type parameter.
+ /// The sixth type parameter.
+ /// The return type parameter.
+ /// The connection to the database. This can either be open or closed.
+ /// The id of the entity in the database.
+ /// The mapping to perform on the entities in the result set.
+ /// The entity with the corresponding id joined with the specified types.
+ public static TReturn Get(this IDbConnection connection,
+ object id,
+ Func map) where TReturn : class
+ {
+ return MultiMap(connection, map, id).FirstOrDefault();
+ }
+
+ ///
+ /// Retrieves the entity of type with the specified id
+ /// joined with the types specified as type parameters.
+ ///
+ /// The first type parameter. This is the source entity.
+ /// The second type parameter.
+ /// The third type parameter.
+ /// The fourth type parameter.
+ /// The fifth type parameter.
+ /// The sixth type parameter.
+ /// The seventh type parameter.
+ /// The return type parameter.
+ /// The connection to the database. This can either be open or closed.
+ /// The id of the entity in the database.
+ /// The mapping to perform on the entities in the result set.
+ /// The entity with the corresponding id joined with the specified types.
+ public static TReturn Get(this IDbConnection connection,
+ object id,
+ Func map) where TReturn : class
+ {
+ return MultiMap(connection, map, id).FirstOrDefault();
+ }
+
+ ///
+ /// Retrieves all the entities of type .
+ ///
+ /// The type of the entity.
+ /// The connection to the database. This can either be open or closed.
+ ///
+ /// A value indicating whether the result of the query should be executed directly,
+ /// or when the query is materialized (using ToList() for example).
+ ///
+ /// A collection of entities of type .
+ public static IEnumerable GetAll(this IDbConnection connection, bool buffered = true) where TEntity : class
+ {
+ var type = typeof (TEntity);
+
+ string sql;
+ if (!_getAllQueryCache.TryGetValue(type, out sql))
+ {
+ var tableName = Resolvers.Table(type);
+ sql = string.Format("select * from {0}", tableName);
+ _getAllQueryCache[type] = sql;
+ }
+
+ return connection.Query(sql: sql, buffered: buffered);
+ }
+
+ ///
+ /// Retrieves all the entities of type
+ /// joined with the types specified as type parameters.
+ ///
+ /// The first type parameter. This is the source entity.
+ /// The second type parameter.
+ /// The return type parameter.
+ /// The connection to the database. This can either be open or closed.
+ /// The mapping to perform on the entities in the result set.
+ ///
+ /// A value indicating whether the result of the query should be executed directly,
+ /// or when the query is materialized (using ToList() for example).
+ ///
+ ///
+ /// A collection of entities of type
+ /// joined with the specified type types.
+ ///
+ public static IEnumerable GetAll(this IDbConnection connection, Func map, bool buffered = true)
+ {
+ return MultiMap(connection, map, buffered: buffered);
+ }
+
+ ///
+ /// Retrieves all the entities of type
+ /// joined with the types specified as type parameters.
+ ///
+ /// The first type parameter. This is the source entity.
+ /// The second type parameter.
+ /// The third type parameter.
+ /// The return type parameter.
+ /// The connection to the database. This can either be open or closed.
+ /// The mapping to perform on the entities in the result set.
+ ///
+ /// A value indicating whether the result of the query should be executed directly,
+ /// or when the query is materialized (using ToList() for example).
+ ///
+ ///
+ /// A collection of entities of type
+ /// joined with the specified type types.
+ ///
+ public static IEnumerable GetAll(this IDbConnection connection, Func map, bool buffered = true)
+ {
+ return MultiMap(connection, map, buffered: buffered);
+ }
+
+ ///
+ /// Retrieves all the entities of type
+ /// joined with the types specified as type parameters.
+ ///
+ /// The first type parameter. This is the source entity.
+ /// The second type parameter.
+ /// The third type parameter.
+ /// The fourth type parameter.
+ /// The return type parameter.
+ /// The connection to the database. This can either be open or closed.
+ /// The mapping to perform on the entities in the result set.
+ ///
+ /// A value indicating whether the result of the query should be executed directly,
+ /// or when the query is materialized (using ToList() for example).
+ ///
+ ///
+ /// A collection of entities of type
+ /// joined with the specified type types.
+ ///
+ public static IEnumerable GetAll(this IDbConnection connection, Func map, bool buffered = true)
+ {
+ return MultiMap(connection, map, buffered: buffered);
+ }
+
+ ///
+ /// Retrieves all the entities of type
+ /// joined with the types specified as type parameters.
+ ///
+ /// The first type parameter. This is the source entity.
+ /// The second type parameter.
+ /// The third type parameter.
+ /// The fourth type parameter.
+ /// The fifth type parameter.
+ /// The return type parameter.
+ /// The connection to the database. This can either be open or closed.
+ /// The mapping to perform on the entities in the result set.
+ ///
+ /// A value indicating whether the result of the query should be executed directly,
+ /// or when the query is materialized (using ToList() for example).
+ ///
+ ///
+ /// A collection of entities of type
+ /// joined with the specified type types.
+ ///
+ public static IEnumerable GetAll(this IDbConnection connection, Func map, bool buffered = true)
+ {
+ return MultiMap(connection, map, buffered: buffered);
+ }
+
+ ///
+ /// Retrieves all the entities of type
+ /// joined with the types specified as type parameters.
+ ///
+ /// The first type parameter. This is the source entity.
+ /// The second type parameter.
+ /// The third type parameter.
+ /// The fourth type parameter.
+ /// The fifth type parameter.
+ /// The sixth type parameter.
+ /// The return type parameter.
+ /// The connection to the database. This can either be open or closed.
+ /// The mapping to perform on the entities in the result set.
+ ///
+ /// A value indicating whether the result of the query should be executed directly,
+ /// or when the query is materialized (using ToList() for example).
+ ///
+ ///
+ /// A collection of entities of type
+ /// joined with the specified type types.
+ ///
+ public static IEnumerable GetAll(this IDbConnection connection, Func map, bool buffered = true)
+ {
+ return MultiMap(connection, map, buffered: buffered);
+ }
+
+ ///
+ /// Retrieves all the entities of type
+ /// joined with the types specified as type parameters.
+ ///
+ /// The first type parameter. This is the source entity.
+ /// The second type parameter.
+ /// The third type parameter.
+ /// The fourth type parameter.
+ /// The fifth type parameter.
+ /// The sixth type parameter.
+ /// The seventh type parameter.
+ /// The return type parameter.
+ /// The connection to the database. This can either be open or closed.
+ /// The mapping to perform on the entities in the result set.
+ ///
+ /// A value indicating whether the result of the query should be executed directly,
+ /// or when the query is materialized (using ToList() for example).
+ ///
+ ///
+ /// A collection of entities of type
+ /// joined with the specified type types.
+ ///
+ public static IEnumerable GetAll(this IDbConnection connection, Func map, bool buffered = true)
+ {
+ return MultiMap(connection, map, buffered: buffered);
+ }
+
+ private static IEnumerable MultiMap(IDbConnection connection, Delegate map, object id = null, bool buffered = true)
+ {
+ var resultType = typeof(TReturn);
+ var resultTableName = Resolvers.Table(resultType);
+ var resultTableKeyColumnName = Resolvers.Column(Resolvers.KeyProperty(resultType));
+
+ var sql = string.Format("select * from {0}", resultTableName);
+
+ var includeTypes = new[]
+ {
+ typeof (T1),
+ typeof (T2),
+ typeof (T3),
+ typeof (T4),
+ typeof (T5),
+ typeof (T6),
+ typeof (T7)
+ }
+ .Where(t => t != typeof (DontMap))
+ .ToArray();
+
+ for (var i = 1; i < includeTypes.Length; i++)
+ {
+ // Determine the table to join with.
+ var sourceType = includeTypes[i - 1];
+ var sourceTableName = Resolvers.Table(sourceType);
+
+ // Determine the table name of the joined table.
+ var includeType = includeTypes[i];
+ var foreignKeyTableName = Resolvers.Table(includeType);
+
+ // Determine the foreign key and the relationship type.
+ ForeignKeyRelation relation;
+ var foreignKeyProperty = Resolvers.ForeignKeyProperty(sourceType, includeType, out relation);
+ var foreignKeyPropertyName = Resolvers.Column(foreignKeyProperty);
+
+ // If the foreign key property is nullable, use a left-join.
+ var joinType = Nullable.GetUnderlyingType(foreignKeyProperty.PropertyType) != null
+ ? "left"
+ : "inner";
+
+ switch (relation)
+ {
+ case ForeignKeyRelation.OneToOne:
+ // Determine the primary key of the foreign key table.
+ var foreignKeyTableKeyColumName = Resolvers.Column(Resolvers.KeyProperty(includeType));
+
+ sql += string.Format(" {0} join {1} on {2}.{3} = {1}.{4}",
+ joinType,
+ foreignKeyTableName,
+ sourceTableName,
+ foreignKeyPropertyName,
+ foreignKeyTableKeyColumName);
+ break;
+
+ case ForeignKeyRelation.OneToMany:
+ // Determine the primary key of the source table.
+ var sourceKeyColumnName = Resolvers.Column(Resolvers.KeyProperty(sourceType));
+
+ sql += string.Format(" {0} join {1} on {2}.{3} = {1}.{4}",
+ joinType,
+ foreignKeyTableName,
+ sourceTableName,
+ sourceKeyColumnName,
+ foreignKeyPropertyName);
+ break;
+
+ case ForeignKeyRelation.ManyToMany:
+ throw new NotImplementedException("Many-to-many relationships are not supported yet.");
+
+ default:
+ throw new NotImplementedException(string.Format("Foreign key relation type '{0}' is not implemented.", relation));
+ }
+ }
+
+ DynamicParameters parameters = null;
+ if (id != null)
+ {
+ sql += string.Format(" where {0}.{1} = @{1}", resultTableName, resultTableKeyColumnName);
+
+ parameters = new DynamicParameters();
+ parameters.Add("Id", id);
+ }
+
+ switch (includeTypes.Length)
+ {
+ case 2:
+ return connection.Query(sql, (Func)map, parameters, buffered: buffered);
+ case 3:
+ return connection.Query(sql, (Func)map, parameters, buffered: buffered);
+ case 4:
+ return connection.Query(sql, (Func)map, parameters, buffered: buffered);
+ case 5:
+ return connection.Query(sql, (Func)map, parameters, buffered: buffered);
+ case 6:
+ return connection.Query(sql, (Func)map, parameters, buffered: buffered);
+ case 7:
+ return connection.Query(sql, (Func)map, parameters, buffered: buffered);
+ }
+
+ throw new InvalidOperationException();
+ }
+
+ private class DontMap
+ {
+ }
+
+ ///
+ /// Selects all the entities matching the specified predicate.
+ ///
+ /// The type of the entity.
+ /// The connection to the database. This can either be open or closed.
+ /// A predicate to filter the results.
+ ///
+ /// A value indicating whether the result of the query should be executed directly,
+ /// or when the query is materialized (using ToList() for example).
+ ///
+ /// A collection of entities of type matching the specified .
+ public static IEnumerable Select(this IDbConnection connection, Expression> predicate, bool buffered = true)
+ {
+ var type = typeof (TEntity);
+
+ string sql;
+ if (!_getAllQueryCache.TryGetValue(type, out sql))
+ {
+ var tableName = Resolvers.Table(type);
+ sql = string.Format("select * from {0}", tableName);
+ _getAllQueryCache[type] = sql;
+ }
+
+ DynamicParameters parameters;
+ sql += new SqlExpression()
+ .Where(predicate)
+ .ToSql(out parameters);
+
+ return connection.Query(sql: sql, param: parameters, buffered: buffered);
+ }
+
+ ///
+ /// Represents a typed SQL expression.
+ ///
+ /// The type of the entity.
+ public class SqlExpression
+ {
+ private readonly StringBuilder _whereBuilder = new StringBuilder();
+ private readonly DynamicParameters _parameters = new DynamicParameters();
+ private int _parameterIndex;
+
+ ///
+ /// Builds a SQL expression for the specified filter expression.
+ ///
+ /// The filter expression on the entity.
+ /// The current instance.
+ public virtual SqlExpression Where(Expression> expression)
+ {
+ AppendToWhere("and", expression);
+ return this;
+ }
+
+ private void AppendToWhere(string conditionOperator, Expression expression)
+ {
+ var sqlExpression = VisitExpression(expression).ToString();
+ AppendToWhere(conditionOperator, sqlExpression);
+ }
+
+ private void AppendToWhere(string conditionOperator, string sqlExpression)
+ {
+ if (_whereBuilder.Length == 0)
+ {
+ _whereBuilder.Append(" where ");
+ }
+ else
+ {
+ _whereBuilder.AppendFormat(" {0} ", conditionOperator);
+ }
+
+ _whereBuilder.Append(sqlExpression);
+ }
+
+ ///
+ /// Visits the expression.
+ ///
+ /// The expression to visit.
+ /// The result of the visit.
+ protected virtual object VisitExpression(Expression expression)
+ {
+ switch (expression.NodeType)
+ {
+ case ExpressionType.Lambda:
+ return VisitLambda(expression as LambdaExpression);
+
+ case ExpressionType.LessThan:
+ case ExpressionType.LessThanOrEqual:
+ case ExpressionType.GreaterThan:
+ case ExpressionType.GreaterThanOrEqual:
+ case ExpressionType.Equal:
+ case ExpressionType.NotEqual:
+ case ExpressionType.And:
+ case ExpressionType.AndAlso:
+ case ExpressionType.Or:
+ case ExpressionType.OrElse:
+ return VisitBinary((BinaryExpression)expression);
+
+ case ExpressionType.Convert:
+ case ExpressionType.Not:
+ return VisitUnary((UnaryExpression)expression);
+
+ case ExpressionType.New:
+ return VisitNew((NewExpression)expression);
+
+ case ExpressionType.MemberAccess:
+ return VisitMemberAccess((MemberExpression)expression);
+
+ case ExpressionType.Constant:
+ return VisitConstantExpression((ConstantExpression)expression);
+ }
+
+ return expression;
+ }
+
+ ///
+ /// Processes a lambda expression.
+ ///
+ /// The lambda expression.
+ /// The result of the processing.
+ protected virtual object VisitLambda(LambdaExpression epxression)
+ {
+ if (epxression.Body.NodeType == ExpressionType.MemberAccess)
+ {
+ var member = epxression.Body as MemberExpression;
+ if (member.Expression != null)
+ {
+ return string.Format("{0} = '1'", VisitMemberAccess(member));
+ }
+ }
+
+ return VisitExpression(epxression.Body);
+ }
+
+ ///
+ /// Processes a binary expression.
+ ///
+ /// The binary expression.
+ /// The result of the processing.
+ protected virtual object VisitBinary(BinaryExpression expression)
+ {
+ object left, right;
+ var operand = BindOperant(expression.NodeType);
+ if (operand == "and" || operand == "or")
+ {
+ // Left side.
+ var member = expression.Left as MemberExpression;
+ if (member != null &&
+ member.Expression != null &&
+ member.Expression.NodeType == ExpressionType.Parameter)
+ {
+ left = string.Format("{0} = '1'", VisitMemberAccess(member));
+ }
+ else
+ {
+ left = VisitExpression(expression.Left);
+ }
+
+ // Right side.
+ member = expression.Right as MemberExpression;
+ if (member != null &&
+ member.Expression != null &&
+ member.Expression.NodeType == ExpressionType.Parameter)
+ {
+ right = string.Format("{0} = '1'", VisitMemberAccess(member));
+ }
+ else
+ {
+ right = VisitExpression(expression.Right);
+ }
+ }
+ else
+ {
+ // It's a single expression.
+ left = VisitExpression(expression.Left);
+ right = VisitExpression(expression.Right);
+
+ var paramName = "p" + _parameterIndex++;
+ _parameters.Add(paramName, value: right);
+ return string.Format("{0} {1} @{2}", left, operand, paramName);
+ }
+
+ return string.Format("{0} {1} {2}", left, operand, right);
+ }
+
+ ///
+ /// Processes a unary expression.
+ ///
+ /// The unary expression.
+ /// The result of the processing.
+ protected virtual object VisitUnary(UnaryExpression expression)
+ {
+ switch (expression.NodeType)
+ {
+ case ExpressionType.Not:
+ var o = VisitExpression(expression.Operand);
+ if (!(o is string))
+ {
+ return !(bool)o;
+ }
+
+ var memberExpression = expression.Operand as MemberExpression;
+ if (memberExpression != null &&
+ Resolvers.Properties(memberExpression.Expression.Type).Any(p => p.Name == (string)o))
+ {
+ o = string.Format("{0} = '1'", o);
+ }
+
+ return string.Format("not ({0})", o);
+ case ExpressionType.Convert:
+ if (expression.Method != null)
+ {
+ return Expression.Lambda(expression).Compile().DynamicInvoke();
+ }
+ break;
+ }
+
+ return VisitExpression(expression.Operand);
+ }
+
+ ///
+ /// Processes a new expression.
+ ///
+ /// The new expression.
+ /// The result of the processing.
+ protected virtual object VisitNew(NewExpression expression)
+ {
+ var member = Expression.Convert(expression, typeof (object));
+ var lambda = Expression.Lambda>(member);
+ var getter = lambda.Compile();
+ return getter();
+ }
+
+ ///
+ /// Processes a member access expression.
+ ///
+ /// The member access expression.
+ /// The result of the processing.
+ protected virtual object VisitMemberAccess(MemberExpression expression)
+ {
+ if (expression.Expression != null && expression.Expression.NodeType == ExpressionType.Parameter)
+ {
+ return MemberToColumn(expression);
+ }
+
+ var member = Expression.Convert(expression, typeof (object));
+ var lambda = Expression.Lambda>(member);
+ var getter = lambda.Compile();
+ return getter();
+ }
+
+ ///
+ /// Processes a constant expression.
+ ///
+ /// The constant expression.
+ /// The result of the processing.
+ protected virtual object VisitConstantExpression(ConstantExpression expression)
+ {
+ return expression.Value ?? "null";
+ }
+
+ ///
+ /// Proccesses a member expression.
+ ///
+ /// The member expression.
+ /// The result of the processing.
+ protected virtual string MemberToColumn(MemberExpression expression)
+ {
+ return Resolvers.Column((PropertyInfo)expression.Member);
+ }
+
+ ///
+ /// Returns the expression operand for the specified expression type.
+ ///
+ /// The expression type for node of an expression tree.
+ /// The expression operand equivalent of the .
+ protected virtual string BindOperant(ExpressionType expressionType)
+ {
+ switch (expressionType)
+ {
+ case ExpressionType.Equal:
+ return "=";
+ case ExpressionType.NotEqual:
+ return "<>";
+ case ExpressionType.GreaterThan:
+ return ">";
+ case ExpressionType.GreaterThanOrEqual:
+ return ">=";
+ case ExpressionType.LessThan:
+ return "<";
+ case ExpressionType.LessThanOrEqual:
+ return "<=";
+ case ExpressionType.AndAlso:
+ return "and";
+ case ExpressionType.OrElse:
+ return "or";
+ case ExpressionType.Add:
+ return "+";
+ case ExpressionType.Subtract:
+ return "-";
+ case ExpressionType.Multiply:
+ return "*";
+ case ExpressionType.Divide:
+ return "/";
+ case ExpressionType.Modulo:
+ return "MOD";
+ case ExpressionType.Coalesce:
+ return "COALESCE";
+ default:
+ return expressionType.ToString();
+ }
+ }
+
+ ///
+ /// Returns the current SQL query.
+ ///
+ /// The current SQL query.
+ public string ToSql()
+ {
+ return _whereBuilder.ToString();
+ }
+
+ ///
+ /// Returns the current SQL query.
+ ///
+ /// When this method returns, contains the parameters for the query.
+ /// The current SQL query.
+ public string ToSql(out DynamicParameters parameters)
+ {
+ parameters = _parameters;
+ return _whereBuilder.ToString();
+ }
+
+ ///
+ /// Returns the current SQL query.
+ ///
+ /// The current SQL query.
+ public override string ToString()
+ {
+ return _whereBuilder.ToString();
+ }
+ }
+
+ ///
+ /// Inserts the specified entity into the database and returns the id.
+ ///
+ /// The type of the entity.
+ /// The connection to the database. This can either be open or closed.
+ /// The entity to be inserted.
+ /// Optional transaction for the command.
+ /// The id of the inserted entity.
+ public static int Insert(this IDbConnection connection, TEntity entity, IDbTransaction transaction = null) where TEntity : class
+ {
+ var type = typeof (TEntity);
+
+ string sql;
+ if (!_insertQueryCache.TryGetValue(type, out sql))
+ {
+ var tableName = Resolvers.Table(type);
+ var keyProperty = Resolvers.KeyProperty(type);
+ var typeProperties = Resolvers.Properties(type).Where(p => p != keyProperty).ToList();
+
+ var columnNames = typeProperties.Select(Resolvers.Column).ToArray();
+ var paramNames = typeProperties.Select(p => "@" + p.Name).ToArray();
+
+ var builder = GetBuilder(connection);
+
+ sql = builder.BuildInsert(tableName, columnNames, paramNames, keyProperty);
+
+ _insertQueryCache[type] = sql;
+ }
+
+ var result = connection.Query(sql, entity, transaction);
+ return result.Single();
+ }
+
+ ///
+ /// Updates the values of the specified entity in the database.
+ /// The return value indicates whether the operation succeeded.
+ ///
+ /// The type of the entity.
+ /// The connection to the database. This can either be open or closed.
+ /// The entity in the database.
+ /// Optional transaction for the command.
+ /// A value indicating whether the update operation succeeded.
+ public static bool Update(this IDbConnection connection, TEntity entity, IDbTransaction transaction = null)
+ {
+ var type = typeof (TEntity);
+
+ string sql;
+ if (!_updateQueryCache.TryGetValue(type, out sql))
+ {
+ var tableName = Resolvers.Table(type);
+ var keyProperty = Resolvers.KeyProperty(type);
+ var typeProperties = Resolvers.Properties(type).Where(p => p != keyProperty).ToList();
+
+ var columnNames = typeProperties.Select(p => string.Format("{0} = @{1}", Resolvers.Column(p), p.Name)).ToArray();
+
+ sql = string.Format("update {0} set {1} where {2} = @{3}",
+ tableName,
+ string.Join(", ", columnNames),
+ Resolvers.Column(keyProperty),
+ keyProperty.Name);
+
+ _updateQueryCache[type] = sql;
+ }
+
+ return connection.Execute(sql, entity, transaction) > 0;
+ }
+
+ ///
+ /// Deletes the specified entity from the database.
+ /// Returns a value indicating whether the operation succeeded.
+ ///
+ /// The type of the entity.
+ /// The connection to the database. This can either be open or closed.
+ /// The entity to be deleted.
+ /// Optional transaction for the command.
+ /// A value indicating whether the delete operation succeeded.
+ public static bool Delete(this IDbConnection connection, TEntity entity, IDbTransaction transaction = null)
+ {
+ var type = typeof (TEntity);
+
+ string sql;
+ if (!_deleteQueryCache.TryGetValue(type, out sql))
+ {
+ var tableName = Resolvers.Table(type);
+ var keyProperty = Resolvers.KeyProperty(type);
+ var keyColumnName = Resolvers.Column(keyProperty);
+
+ sql = string.Format("delete from {0} where {1} = @{2}", tableName, keyColumnName, keyProperty.Name);
+
+ _deleteQueryCache[type] = sql;
+ }
+
+ return connection.Execute(sql, entity, transaction) > 0;
+ }
+
+ ///
+ /// Helper class for retrieving type metadata to build sql queries using configured resolvers.
+ ///
+ public static class Resolvers
+ {
+ private static readonly IDictionary _typeTableNameCache = new Dictionary();
+ private static readonly IDictionary _columnNameCache = new Dictionary();
+ private static readonly IDictionary _typeKeyPropertyCache = new Dictionary();
+ private static readonly IDictionary _typePropertiesCache = new Dictionary();
+ private static readonly IDictionary _typeForeignKeyPropertyCache = new Dictionary();
+
+ ///
+ /// Gets the key property for the specified type, using the configured .
+ ///
+ /// The to get the key property for.
+ /// The key property for .
+ public static PropertyInfo KeyProperty(Type type)
+ {
+ PropertyInfo keyProperty;
+ if (!_typeKeyPropertyCache.TryGetValue(type, out keyProperty))
+ {
+ keyProperty = _keyPropertyResolver.ResolveKeyProperty(type);
+ _typeKeyPropertyCache[type] = keyProperty;
+ }
+
+ return keyProperty;
+ }
+
+ ///
+ /// Gets the foreign key property for the specified source type and including type
+ /// using the configure d.
+ ///
+ /// The source type which should contain the foreign key property.
+ /// The type of the foreign key relation.
+ /// The foreign key relationship type.
+ /// The foreign key property for and .
+ public static PropertyInfo ForeignKeyProperty(Type sourceType, Type includingType, out ForeignKeyRelation foreignKeyRelation)
+ {
+ var key = string.Format("{0};{1}", sourceType.FullName, includingType.FullName);
+
+ ForeignKeyInfo foreignKeyInfo;
+ if (!_typeForeignKeyPropertyCache.TryGetValue(key, out foreignKeyInfo))
+ {
+ // Resole the property and relation.
+ var foreignKeyProperty = _foreignKeyPropertyResolver.ResolveForeignKeyProperty(sourceType, includingType, out foreignKeyRelation);
+
+ // Cache the info.
+ foreignKeyInfo = new ForeignKeyInfo(foreignKeyProperty, foreignKeyRelation);
+ _typeForeignKeyPropertyCache[key] = foreignKeyInfo;
+ }
+
+ foreignKeyRelation = foreignKeyInfo.Relation;
+ return foreignKeyInfo.PropertyInfo;
+ }
+
+ private class ForeignKeyInfo
+ {
+ public ForeignKeyInfo(PropertyInfo propertyInfo, ForeignKeyRelation relation)
+ {
+ PropertyInfo = propertyInfo;
+ Relation = relation;
+ }
+
+ public PropertyInfo PropertyInfo { get; private set; }
+
+ public ForeignKeyRelation Relation { get; private set; }
+ }
+
+ ///
+ /// Gets the properties to be mapped for the specified type, using the configured .
+ ///
+ /// The to get the properties from.
+ /// >The collection of to be mapped properties of .
+ public static IEnumerable Properties(Type type)
+ {
+ PropertyInfo[] properties;
+ if (!_typePropertiesCache.TryGetValue(type, out properties))
+ {
+ properties = _propertyResolver.ResolveProperties(type).ToArray();
+ _typePropertiesCache[type] = properties;
+ }
+
+ return properties;
+ }
+
+ ///
+ /// Gets the name of the table in the database for the specified type,
+ /// using the configured .
+ ///
+ /// The to get the table name for.
+ /// The table name in the database for .
+ public static string Table(Type type)
+ {
+ string name;
+ if (!_typeTableNameCache.TryGetValue(type, out name))
+ {
+ name = _tableNameResolver.ResolveTableName(type);
+ _typeTableNameCache[type] = name;
+ }
+ return name;
+ }
+
+ ///
+ /// Gets the name of the column in the database for the specified type,
+ /// using the configured .
+ ///
+ /// The to get the column name for.
+ /// The column name in the database for .
+ public static string Column(PropertyInfo propertyInfo)
+ {
+ var key = string.Format("{0}.{1}", propertyInfo.DeclaringType, propertyInfo.Name);
+
+ string columnName;
+ if (!_columnNameCache.TryGetValue(key, out columnName))
+ {
+ columnName = _columnNameResolver.ResolveColumnName(propertyInfo);
+ _columnNameCache[key] = columnName;
+ }
+
+ return columnName;
+ }
+
+ ///
+ /// Provides access to default resolver implementations.
+ ///
+ public static class Default
+ {
+ ///
+ /// The default column name resolver.
+ ///
+ public static readonly IColumnNameResolver ColumnNameResolver = new DefaultColumnNameResolver();
+
+ ///
+ /// The default property resolver.
+ ///
+ public static readonly IPropertyResolver PropertyResolver = new DefaultPropertyResolver();
+
+ ///
+ /// The default key property resolver.
+ ///
+ public static readonly IKeyPropertyResolver KeyPropertyResolver = new DefaultKeyPropertyResolver();
+
+ ///
+ /// The default table name resolver.
+ ///
+ public static readonly ITableNameResolver TableNameResolver = new DefaultTableNameResolver();
+ }
+ }
+
+ #region Property resolving
+ private static IPropertyResolver _propertyResolver = new DefaultPropertyResolver();
+
+ ///
+ /// Defines methods for resolving the properties of entities.
+ /// Custom implementations can be registerd with .
+ ///
+ public interface IPropertyResolver
+ {
+ ///
+ /// Resolves the properties to be mapped for the specified type.
+ ///
+ /// The type to resolve the properties to be mapped for.
+ /// A collection of 's of the .
+ IEnumerable ResolveProperties(Type type);
+ }
+
+ ///
+ /// Sets the implementation for resolving key of entities.
+ ///
+ /// An instance of .
+ public static void SetPropertyResolver(IPropertyResolver propertyResolver)
+ {
+ _propertyResolver = propertyResolver;
+ }
+
+ ///
+ /// Represents the base class for property resolvers.
+ ///
+ public abstract class PropertyResolverBase : IPropertyResolver
+ {
+ private static readonly HashSet _primitiveTypes = new HashSet
+ {
+ typeof (object),
+ typeof (string),
+ typeof(Guid),
+ typeof (decimal),
+ typeof (double),
+ typeof (float),
+ typeof (DateTime),
+ typeof (TimeSpan)
+ };
+
+ ///
+ /// Resolves the properties to be mapped for the specified type.
+ ///
+ /// The type to resolve the properties to be mapped for.
+ /// A collection of 's of the .
+ public abstract IEnumerable ResolveProperties(Type type);
+
+ ///
+ /// Gets a collection of types that are considered 'primitive' for Dommel but are not for the CLR.
+ /// Override this if you need your own implementation of this.
+ ///
+ protected virtual HashSet PrimitiveTypes
+ {
+ get
+ {
+ return _primitiveTypes;
+ }
+ }
+
+ ///
+ /// Filters the complex types from the specified collection of properties.
+ ///
+ /// A collection of properties.
+ /// The properties that are considered 'primitive' of .
+ protected virtual IEnumerable FilterComplexTypes(IEnumerable properties)
+ {
+ foreach (var property in properties)
+ {
+ var type = property.PropertyType;
+ type = Nullable.GetUnderlyingType(type) ?? type;
+
+#if DOTNET5_4
+ if (type.GetTypeInfo().IsPrimitive || type.GetTypeInfo().IsEnum || PrimitiveTypes.Contains(type))
+#else
+ if (type.IsPrimitive || type.IsEnum || PrimitiveTypes.Contains(type))
+#endif
+ {
+ yield return property;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Default implemenation of the interface.
+ ///
+ public class DefaultPropertyResolver : PropertyResolverBase
+ {
+ public override IEnumerable ResolveProperties(Type type)
+ {
+ return FilterComplexTypes(type.GetProperties());
+ }
+ }
+ #endregion
+
+ #region Key property resolving
+ private static IKeyPropertyResolver _keyPropertyResolver = new DefaultKeyPropertyResolver();
+
+ ///
+ /// Sets the implementation for resolving key properties of entities.
+ ///
+ /// An instance of .
+ public static void SetKeyPropertyResolver(IKeyPropertyResolver resolver)
+ {
+ _keyPropertyResolver = resolver;
+ }
+
+ ///
+ /// Defines methods for resolving the key property of entities.
+ /// Custom implementations can be registerd with .
+ ///
+ public interface IKeyPropertyResolver
+ {
+ ///
+ /// Resolves the key property for the specified type.
+ ///
+ /// The type to resolve the key property for.
+ /// A instance of the key property of .
+ PropertyInfo ResolveKeyProperty(Type type);
+ }
+
+ ///
+ /// Implements the interface by resolving key properties
+ /// with the [Key] attribute or with the name 'Id'.
+ ///
+ public class DefaultKeyPropertyResolver : IKeyPropertyResolver
+ {
+ ///
+ /// Finds the key property by looking for a property with the [Key] attribute or with the name 'Id'.
+ ///
+ public virtual PropertyInfo ResolveKeyProperty(Type type)
+ {
+ var allProps = Resolvers.Properties(type).ToList();
+
+ // Look for properties with the [Key] attribute.
+ var keyProps = allProps.Where(p => p.GetCustomAttributes(true).Any(a => a is KeyAttribute)).ToList();
+
+ if (keyProps.Count == 0)
+ {
+ // Search for properties named as 'Id' as fallback.
+ keyProps = allProps.Where(p => p.Name.Equals("Id", StringComparison.OrdinalIgnoreCase)).ToList();
+ }
+
+ if (keyProps.Count == 0)
+ {
+ throw new Exception(string.Format("Could not find the key property for type '{0}'.", type.FullName));
+ }
+
+ if (keyProps.Count > 1)
+ {
+ throw new Exception(string.Format("Multiple key properties were found for type '{0}'.", type.FullName));
+ }
+
+ return keyProps[0];
+ }
+ }
+ #endregion
+
+ #region Foreign key property resolving
+ ///
+ /// Describes a foreign key relationship.
+ ///
+ public enum ForeignKeyRelation
+ {
+ ///
+ /// Specifies a one-to-one relationship.
+ ///
+ OneToOne,
+
+ ///
+ /// Specifies a one-to-many relationship.
+ ///
+ OneToMany,
+
+ ///
+ /// Specifies a many-to-many relationship.
+ ///
+ ManyToMany
+ }
+
+ private static IForeignKeyPropertyResolver _foreignKeyPropertyResolver = new DefaultForeignKeyPropertyResolver();
+
+ ///
+ /// Sets the implementation for resolving foreign key properties.
+ ///
+ /// An instance of .
+ public static void SetForeignKeyPropertyResolver(IForeignKeyPropertyResolver resolver)
+ {
+ _foreignKeyPropertyResolver = resolver;
+ }
+
+ ///
+ /// Defines methods for resolving foreign key properties.
+ ///
+ public interface IForeignKeyPropertyResolver
+ {
+ ///
+ /// Resolves the foreign key property for the specified source type and including type.
+ ///
+ /// The source type which should contain the foreign key property.
+ /// The type of the foreign key relation.
+ /// The foreign key relationship type.
+ /// The foreign key property for and .
+ PropertyInfo ResolveForeignKeyProperty(Type sourceType, Type includingType, out ForeignKeyRelation foreignKeyRelation);
+ }
+
+ ///
+ /// Implements the interface.
+ ///
+ public class DefaultForeignKeyPropertyResolver : IForeignKeyPropertyResolver
+ {
+ ///
+ /// Resolves the foreign key property for the specified source type and including type
+ /// by using + Id as property name.
+ ///
+ /// The source type which should contain the foreign key property.
+ /// The type of the foreign key relation.
+ /// The foreign key relationship type.
+ /// The foreign key property for and .
+ public virtual PropertyInfo ResolveForeignKeyProperty(Type sourceType, Type includingType, out ForeignKeyRelation foreignKeyRelation)
+ {
+ var oneToOneFk = ResolveOneToOne(sourceType, includingType);
+ if (oneToOneFk != null)
+ {
+ foreignKeyRelation = ForeignKeyRelation.OneToOne;
+ return oneToOneFk;
+ }
+
+ var oneToManyFk = ResolveOneToMany(sourceType, includingType);
+ if (oneToManyFk != null)
+ {
+ foreignKeyRelation = ForeignKeyRelation.OneToMany;
+ return oneToManyFk;
+ }
+
+ var msg = string.Format("Could not resolve foreign key property. Source type '{0}'; including type: '{1}'.", sourceType.FullName, includingType.FullName);
+ throw new Exception(msg);
+ }
+
+ private static PropertyInfo ResolveOneToOne(Type sourceType, Type includingType)
+ {
+ // Look for the foreign key on the source type.
+ var foreignKeyName = includingType.Name + "Id";
+ var foreignKeyProperty = sourceType.GetProperties().FirstOrDefault(p => p.Name == foreignKeyName);
+
+ return foreignKeyProperty;
+ }
+
+ private static PropertyInfo ResolveOneToMany(Type sourceType, Type includingType)
+ {
+ // Look for the foreign key on the including type.
+ var foreignKeyName = sourceType.Name + "Id";
+ var foreignKeyProperty = includingType.GetProperties().FirstOrDefault(p => p.Name == foreignKeyName);
+ return foreignKeyProperty;
+ }
+ }
+ #endregion
+
+ #region Table name resolving
+ private static ITableNameResolver _tableNameResolver = new DefaultTableNameResolver();
+
+ ///
+ /// Sets the implementation for resolving table names for entities.
+ ///
+ /// An instance of .
+ public static void SetTableNameResolver(ITableNameResolver resolver)
+ {
+ _tableNameResolver = resolver;
+ }
+
+ ///
+ /// Defines methods for resolving table names of entities.
+ /// Custom implementations can be registerd with .
+ ///
+ public interface ITableNameResolver
+ {
+ ///
+ /// Resolves the table name for the specified type.
+ ///
+ /// The type to resolve the table name for.
+ /// A string containing the resolved table name for for .
+ string ResolveTableName(Type type);
+ }
+
+ ///
+ /// Implements the interface by resolving table names
+ /// by making the type name plural and removing the 'I' prefix for interfaces.
+ ///
+ public class DefaultTableNameResolver : ITableNameResolver
+ {
+ ///
+ /// Resolves the table name by making the type plural (+ 's', Product -> Products)
+ /// and removing the 'I' prefix for interfaces.
+ ///
+ public virtual string ResolveTableName(Type type)
+ {
+ var name = type.Name + "s";
+#if DOTNET5_4
+ if (type.GetTypeInfo().IsInterface && name.StartsWith("I"))
+#else
+ if (type.IsInterface && name.StartsWith("I"))
+#endif
+ {
+ name = name.Substring(1);
+ }
+
+ // todo: add [Table] attribute support.
+ return name;
+ }
+ }
+ #endregion
+
+ #region Column name resolving
+ private static IColumnNameResolver _columnNameResolver = new DefaultColumnNameResolver();
+
+ ///
+ /// Sets the implementation for resolving column names.
+ ///
+ /// An instance of .
+ public static void SetColumnNameResolver(IColumnNameResolver resolver)
+ {
+ _columnNameResolver = resolver;
+ }
+
+ ///
+ /// Defines methods for resolving column names for entities.
+ /// Custom implementations can be registerd with .
+ ///
+ public interface IColumnNameResolver
+ {
+ ///
+ /// Resolves the column name for the specified property.
+ ///
+ /// The property of the entity.
+ /// The column name for the property.
+ string ResolveColumnName(PropertyInfo propertyInfo);
+ }
+
+ ///
+ /// Implements the .
+ ///
+ public class DefaultColumnNameResolver : IColumnNameResolver
+ {
+ ///
+ /// Resolves the column name for the property. This is just the name of the property.
+ ///
+ public virtual string ResolveColumnName(PropertyInfo propertyInfo)
+ {
+ return propertyInfo.Name;
+ }
+ }
+ #endregion
+
+ #region Sql builders
+ ///
+ /// Adds a custom implementation of
+ /// for the specified ADO.NET connection type.
+ ///
+ ///
+ /// The ADO.NET conncetion type to use the with.
+ /// Example: typeof(SqlConnection).
+ ///
+ /// An implementation of the .
+ public static void AddSqlBuilder(Type connectionType, ISqlBuilder builder)
+ {
+ _sqlBuilders[connectionType.Name.ToLower()] = builder;
+ }
+
+ private static ISqlBuilder GetBuilder(IDbConnection connection)
+ {
+ var connectionName = connection.GetType().Name.ToLower();
+ ISqlBuilder builder;
+ return _sqlBuilders.TryGetValue(connectionName, out builder) ? builder : new SqlServerSqlBuilder();
+ }
+
+ ///
+ /// Defines methods for building specialized SQL queries.
+ ///
+ public interface ISqlBuilder
+ {
+ ///
+ /// Builds an insert query using the specified table name, column names and parameter names.
+ /// A query to fetch the new id will be included as well.
+ ///
+ /// The name of the table to query.
+ /// The names of the columns in the table.
+ /// The names of the parameters in the database command.
+ /// The key property. This can be used to query a specific column for the new id. This is optional.
+ /// An insert query including a query to fetch the new id.
+ string BuildInsert(string tableName, string[] columnNames, string[] paramNames, PropertyInfo keyProperty);
+ }
+
+ private sealed class SqlServerSqlBuilder : ISqlBuilder
+ {
+ public string BuildInsert(string tableName, string[] columnNames, string[] paramNames, PropertyInfo keyProperty)
+ {
+ return string.Format("set nocount on insert into {0} ({1}) values ({2}) select cast(scope_identity() as int)",
+ tableName,
+ string.Join(", ", columnNames),
+ string.Join(", ", paramNames));
+ }
+ }
+
+ private sealed class SqlServerCeSqlBuilder : ISqlBuilder
+ {
+ public string BuildInsert(string tableName, string[] columnNames, string[] paramNames, PropertyInfo keyProperty)
+ {
+ return string.Format("insert into {0} ({1}) values ({2}) select cast(@@IDENTITY as int)",
+ tableName,
+ string.Join(", ", columnNames),
+ string.Join(", ", paramNames));
+ }
+ }
+
+ private sealed class SqliteSqlBuilder : ISqlBuilder
+ {
+ public string BuildInsert(string tableName, string[] columnNames, string[] paramNames, PropertyInfo keyProperty)
+ {
+ return string.Format("insert into {0} ({1}) values ({2}); select last_insert_rowid() id",
+ tableName,
+ string.Join(", ", columnNames),
+ string.Join(", ", paramNames));
+ }
+ }
+
+ private sealed class MySqlSqlBuilder : ISqlBuilder
+ {
+ public string BuildInsert(string tableName, string[] columnNames, string[] paramNames, PropertyInfo keyProperty)
+ {
+ return string.Format("insert into {0} ({1}) values ({2}) select LAST_INSERT_ID() id",
+ tableName,
+ string.Join(", ", columnNames),
+ string.Join(", ", paramNames));
+ }
+ }
+
+ private sealed class PostgresSqlBuilder : ISqlBuilder
+ {
+ public string BuildInsert(string tableName, string[] columnNames, string[] paramNames, PropertyInfo keyProperty)
+ {
+ var sql = string.Format("insert into {0} ({1}) values ({2}) select last_insert_rowid() id",
+ tableName,
+ string.Join(", ", columnNames),
+ string.Join(", ", paramNames));
+
+ if (keyProperty != null)
+ {
+ var keyColumnName = Resolvers.Column(keyProperty);
+
+ sql += " RETURNING " + keyColumnName;
+ }
+ else
+ {
+ // todo: what behavior is desired here?
+ throw new Exception("A key property is required for the PostgresSqlBuilder.");
+ }
+
+ return sql;
+ }
+ }
+ #endregion
+ }
+}
diff --git a/src/Dommel/project.json b/src/Dommel/project.json
index aa3bb579..22117efd 100644
--- a/src/Dommel/project.json
+++ b/src/Dommel/project.json
@@ -1,44 +1,42 @@
-{
- "version": "1.4.2",
- "copyright": "© 2015 Henk Mollema",
- "description": "Simple CRUD operations for Dapper.",
- "authors": [ "henkmollema" ],
- "tags": [ "dommel", "crud", "dapper", "database", "orm", "ado.net" ],
- "projectUrl": "https://github.com/henkmollema/Dommel",
- "licenseUrl": "http://opensource.org/licenses/MIT",
- "compile": [ "../Dommel-net40/*.cs" ],
- "dependencies": {
- "Dapper": "1.41-beta5"
- },
- "frameworks": {
- "net40": {
- "frameworkAssemblies": {
- "System.ComponentModel.DataAnnotations": "4.0.0.0",
- "System.Data": "4.0.0.0"
- }
- },
- "net45": {
- "frameworkAssemblies": {
- "System.ComponentModel.DataAnnotations": "4.0.0.0",
- "System.Data": "4.0.0.0"
- }
- },
- "dnx451": {
- "frameworkAssemblies": {
- "System.ComponentModel.DataAnnotations": "4.0.0.0",
- "System.Data": "4.0.0.0"
- }
- },
- "dnxcore50": {
- "dependencies": {
- "Microsoft.CSharp": "4.0.0",
- "System.Collections": "4.0.10",
- "System.ComponentModel.Annotations": "4.0.10",
- "System.Data.Common": "4.0.0",
- "System.Linq": "4.0.0",
- "System.Reflection": "4.0.10",
- "System.Reflection.Extensions": "4.0.0"
- }
- }
- }
-}
+{
+ "version": "1.5.0",
+ "copyright": "© 2016 Henk Mollema",
+ "description": "Simple CRUD operations for Dapper.",
+ "authors": [ "henkmollema" ],
+ "tags": [ "dommel", "crud", "dapper", "database", "orm", "ado.net" ],
+ "projectUrl": "https://github.com/henkmollema/Dommel",
+ "licenseUrl": "http://opensource.org/licenses/MIT",
+ "dependencies": {
+ "Dapper": "1.50.0-beta8"
+ },
+ "frameworks": {
+ "net40": {
+ "frameworkAssemblies": {
+ "System.ComponentModel.DataAnnotations": "4.0.0.0",
+ "System.Data": "4.0.0.0"
+ }
+ },
+ "net45": {
+ "frameworkAssemblies": {
+ "System.ComponentModel.DataAnnotations": "4.0.0.0",
+ "System.Data": "4.0.0.0"
+ }
+ },
+ "net451": {
+ "frameworkAssemblies": {
+ "System.ComponentModel.DataAnnotations": "4.0.0.0",
+ "System.Data": "4.0.0.0"
+ }
+ },
+ "dotnet5.4": {
+ "dependencies": {
+ "Microsoft.CSharp": "4.0.0",
+ "System.Collections": "4.0.10",
+ "System.ComponentModel.Annotations": "4.0.10",
+ "System.Data.Common": "4.0.0",
+ "System.Linq": "4.0.0",
+ "System.Reflection.Extensions": "4.0.0"
+ }
+ }
+ }
+}