/PetaPoco/PetaPoco.cs
C# | 3170 lines | 2583 code | 399 blank | 188 comment | 417 complexity | 696b61070b94f81604e01f77bf97bc69 MD5 | raw file
Possible License(s): Apache-2.0
Large files files are truncated, but you can click here to view the full file
- /* PetaPoco v4.0.3.12 - A Tiny ORMish thing for your POCO's.
- * Copyright 2011-2012 Topten Software. All Rights Reserved.
- *
- * Apache License 2.0 - http://www.toptensoftware.com/petapoco/license
- *
- * Special thanks to Rob Conery (@robconery) for original inspiration (ie:Massive) and for
- * use of Subsonic's T4 templates, Rob Sullivan (@DataChomp) for hard core DBA advice
- * and Adam Schroder (@schotime) for lots of suggestions, improvements and Oracle support
- */
-
- //#define PETAPOCO_NO_DYNAMIC //in your project settings on .NET 3.5
-
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Configuration;
- using System.Data.Common;
- using System.Data;
- using System.Text.RegularExpressions;
- using System.Reflection;
- using System.Reflection.Emit;
- using System.Linq.Expressions;
- using System.Threading;
-
- namespace PetaPoco
- {
- // Poco's marked [Explicit] require all column properties to be marked
- [AttributeUsage(AttributeTargets.Class)]
- public class ExplicitColumnsAttribute : Attribute
- {
- }
- // For non-explicit pocos, causes a property to be ignored
- [AttributeUsage(AttributeTargets.Property)]
- public class IgnoreAttribute : Attribute
- {
- }
-
- // For explicit pocos, marks property as a column
- [AttributeUsage(AttributeTargets.Property)]
- public class ColumnAttribute : Attribute
- {
- public ColumnAttribute() { }
- public ColumnAttribute(string name) { Name = name; }
- public string Name { get; set; }
- }
-
- // For explicit pocos, marks property as a column
- [AttributeUsage(AttributeTargets.Property)]
- public class ResultColumnAttribute : ColumnAttribute
- {
- public ResultColumnAttribute() { }
- public ResultColumnAttribute(string name) : base(name) { }
- }
-
- // Specify the table name of a poco
- [AttributeUsage(AttributeTargets.Class)]
- public class TableNameAttribute : Attribute
- {
- public TableNameAttribute(string tableName)
- {
- Value = tableName;
- }
- public string Value { get; private set; }
- }
-
- // Specific the primary key of a poco class (and optional sequence name for Oracle)
- [AttributeUsage(AttributeTargets.Class)]
- public class PrimaryKeyAttribute : Attribute
- {
- public PrimaryKeyAttribute(string primaryKey)
- {
- Value = primaryKey;
- autoIncrement = true;
- }
-
- public string Value { get; private set; }
- public string sequenceName { get; set; }
- public bool autoIncrement { get; set; }
- }
-
- [AttributeUsage(AttributeTargets.Property)]
- public class AutoJoinAttribute : Attribute
- {
- public AutoJoinAttribute() { }
- }
-
- [AttributeUsage(AttributeTargets.Property)]
- public class VersionColumnAttribute : ColumnAttribute
- {
- public VersionColumnAttribute() {}
- public VersionColumnAttribute(string name) : base(name) { }
- }
-
- // Results from paged request
- public class Page<T>
- {
- public long CurrentPage { get; set; }
- public long TotalPages { get; set; }
- public long TotalItems { get; set; }
- public long ItemsPerPage { get; set; }
- public List<T> Items { get; set; }
- public object Context { get; set; }
- }
-
- // Pass as parameter value to force to DBType.AnsiString
- public class AnsiString
- {
- public AnsiString(string str)
- {
- Value = str;
- }
- public string Value { get; private set; }
- }
-
- // Used by IMapper to override table bindings for an object
- public class TableInfo
- {
- public string TableName { get; set; }
- public string PrimaryKey { get; set; }
- public bool AutoIncrement { get; set; }
- public string SequenceName { get; set; }
- }
-
- // Optionally provide an implementation of this to Database.Mapper
- public interface IMapper
- {
- void GetTableInfo(Type t, TableInfo ti);
- bool MapPropertyToColumn(PropertyInfo pi, ref string columnName, ref bool resultColumn);
- Func<object, object> GetFromDbConverter(PropertyInfo pi, Type SourceType);
- Func<object, object> GetToDbConverter(Type SourceType);
- }
-
- // This will be merged with IMapper in the next major version
- public interface IMapper2 : IMapper
- {
- Func<object, object> GetFromDbConverter(Type DestType, Type SourceType);
- }
-
- public abstract class DefaultMapper : IMapper2
- {
- public virtual void GetTableInfo(Type t, TableInfo ti) { }
- public virtual bool MapPropertyToColumn(PropertyInfo pi, ref string columnName, ref bool resultColumn)
- {
- return true;
- }
- public virtual Func<object, object> GetFromDbConverter(PropertyInfo pi, Type SourceType)
- {
- return GetFromDbConverter(pi.PropertyType, SourceType);
- }
- public virtual Func<object, object> GetToDbConverter(Type SourceType)
- {
- return null;
- }
- public virtual Func<object, object> GetFromDbConverter(Type DestType, Type SourceType)
- {
- return null;
- }
- }
-
- public interface IDatabaseQuery
- {
- void OpenSharedConnection();
- void CloseSharedConnection();
- int Execute(string sql, params object[] args);
- int Execute(Sql sql);
- T ExecuteScalar<T>(string sql, params object[] args);
- T ExecuteScalar<T>(Sql sql);
- List<T> Fetch<T>();
- List<T> Fetch<T>(string sql, params object[] args);
- List<T> Fetch<T>(Sql sql);
- List<T> Fetch<T>(long page, long itemsPerPage, string sql, params object[] args);
- List<T> Fetch<T>(long page, long itemsPerPage, Sql sql);
- Page<T> Page<T>(long page, long itemsPerPage, string sql, params object[] args);
- Page<T> Page<T>(long page, long itemsPerPage, Sql sql);
- List<T> SkipTake<T>(long skip, long take, string sql, params object[] args);
- List<T> SkipTake<T>(long skip, long take, Sql sql);
- List<TRet> Fetch<T1, T2, TRet>(Func<T1, T2, TRet> cb, string sql, params object[] args);
- List<TRet> Fetch<T1, T2, T3, TRet>(Func<T1, T2, T3, TRet> cb, string sql, params object[] args);
- List<TRet> Fetch<T1, T2, T3, T4, TRet>(Func<T1, T2, T3, T4, TRet> cb, string sql, params object[] args);
- IEnumerable<TRet> Query<T1, T2, TRet>(Func<T1, T2, TRet> cb, string sql, params object[] args);
- IEnumerable<TRet> Query<T1, T2, T3, TRet>(Func<T1, T2, T3, TRet> cb, string sql, params object[] args);
- IEnumerable<TRet> Query<T1, T2, T3, T4, TRet>(Func<T1, T2, T3, T4, TRet> cb, string sql, params object[] args);
- List<TRet> Fetch<T1, T2, TRet>(Func<T1, T2, TRet> cb, Sql sql);
- List<TRet> Fetch<T1, T2, T3, TRet>(Func<T1, T2, T3, TRet> cb, Sql sql);
- List<TRet> Fetch<T1, T2, T3, T4, TRet>(Func<T1, T2, T3, T4, TRet> cb, Sql sql);
- IEnumerable<TRet> Query<T1, T2, TRet>(Func<T1, T2, TRet> cb, Sql sql);
- IEnumerable<TRet> Query<T1, T2, T3, TRet>(Func<T1, T2, T3, TRet> cb, Sql sql);
- IEnumerable<TRet> Query<T1, T2, T3, T4, TRet>(Func<T1, T2, T3, T4, TRet> cb, Sql sql);
- List<T1> Fetch<T1, T2>(string sql, params object[] args);
- List<T1> Fetch<T1, T2, T3>(string sql, params object[] args);
- List<T1> Fetch<T1, T2, T3, T4>(string sql, params object[] args);
- IEnumerable<T1> Query<T1, T2>(string sql, params object[] args);
- IEnumerable<T1> Query<T1, T2, T3>(string sql, params object[] args);
- IEnumerable<T1> Query<T1, T2, T3, T4>(string sql, params object[] args);
- IEnumerable<TRet> Query<TRet>(Type[] types, object cb, Sql sql);
- List<T1> Fetch<T1, T2>(Sql sql);
- List<T1> Fetch<T1, T2, T3>(Sql sql);
- List<T1> Fetch<T1, T2, T3, T4>(Sql sql);
- IEnumerable<T1> Query<T1, T2>(Sql sql);
- IEnumerable<T1> Query<T1, T2, T3>(Sql sql);
- IEnumerable<T1> Query<T1, T2, T3, T4>(Sql sql);
- IEnumerable<T> Query<T>(string sql, params object[] args);
- IEnumerable<T> Query<T>(Sql sql);
- T SingleById<T>(object primaryKey);
- T SingleOrDefaultById<T>(object primaryKey);
- T Single<T>(string sql, params object[] args);
- T SingleInto<T>(T instance, string sql, params object[] args);
- T SingleOrDefault<T>(string sql, params object[] args);
- T SingleOrDefaultInto<T>(T instance, string sql, params object[] args);
- T First<T>(string sql, params object[] args);
- T FirstInto<T>(T instance, string sql, params object[] args);
- T FirstOrDefault<T>(string sql, params object[] args);
- T FirstOrDefaultInto<T>(T instance, string sql, params object[] args);
- T Single<T>(Sql sql);
- T SingleInto<T>(T instance, Sql sql);
- T SingleOrDefault<T>(Sql sql);
- T SingleOrDefaultInto<T>(T instance, Sql sql);
- T First<T>(Sql sql);
- T FirstInto<T>(T instance, Sql sql);
- T FirstOrDefault<T>(Sql sql);
- T FirstOrDefaultInto<T>(T instance, Sql sql);
- Dictionary<TKey, TValue> Dictionary<TKey, TValue>(Sql Sql);
- Dictionary<TKey, TValue> Dictionary<TKey, TValue>(string sql, params object[] args);
- bool Exists<T>(object primaryKey);
- int OneTimeCommandTimeout { get; set; }
-
- TRet FetchMultiple<T1, T2, TRet>(Func<List<T1>, List<T2>, TRet> cb, string sql, params object[] args);
- TRet FetchMultiple<T1, T2, T3, TRet>(Func<List<T1>, List<T2>, List<T3>, TRet> cb, string sql, params object[] args);
- TRet FetchMultiple<T1, T2, T3, T4, TRet>(Func<List<T1>, List<T2>, List<T3>, List<T4>, TRet> cb, string sql, params object[] args);
- TRet FetchMultiple<T1, T2, TRet>(Func<List<T1>, List<T2>, TRet> cb, Sql sql);
- TRet FetchMultiple<T1, T2, T3, TRet>(Func<List<T1>, List<T2>, List<T3>, TRet> cb, Sql sql);
- TRet FetchMultiple<T1, T2, T3, T4, TRet>(Func<List<T1>, List<T2>, List<T3>, List<T4>, TRet> cb, Sql sql);
- #if PETAPOCO_NO_DYNAMIC
- Database.Tuple<List<T1>, List<T2>> FetchMultiple<T1, T2>(string sql, params object[] args);
- Database.Tuple<List<T1>, List<T2>, List<T3>> FetchMultiple<T1, T2, T3>(string sql, params object[] args);
- Database.Tuple<List<T1>, List<T2>, List<T3>, List<T4>> FetchMultiple<T1, T2, T3, T4>(string sql, params object[] args);
- Database.Tuple<List<T1>, List<T2>> FetchMultiple <T1, T2>(Sql sql);
- Database.Tuple<List<T1>, List<T2>, List<T3>> FetchMultiple <T1, T2, T3>(Sql sql);
- Database.Tuple<List<T1>, List<T2>, List<T3>, List<T4>> FetchMultiple <T1, T2, T3, T4>(Sql sql);
- #else
- Tuple<List<T1>, List<T2>> FetchMultiple<T1, T2>(string sql, params object[] args);
- Tuple<List<T1>, List<T2>, List<T3>> FetchMultiple<T1, T2, T3>(string sql, params object[] args);
- Tuple<List<T1>, List<T2>, List<T3>, List<T4>> FetchMultiple<T1, T2, T3, T4>(string sql, params object[] args);
- Tuple<List<T1>, List<T2>> FetchMultiple<T1, T2>(Sql sql);
- Tuple<List<T1>, List<T2>, List<T3>> FetchMultiple<T1, T2, T3>(Sql sql);
- Tuple<List<T1>, List<T2>, List<T3>, List<T4>> FetchMultiple<T1, T2, T3, T4>(Sql sql);
- #endif
- }
-
- public interface IDatabase : IDatabaseQuery
- {
- void Dispose();
- IDbConnection Connection { get; }
- IDbTransaction Transaction { get; }
- IDataParameter CreateParameter();
- Transaction GetTransaction();
- Transaction GetTransaction(IsolationLevel? isolationLevel);
- void BeginTransaction();
- void BeginTransaction(IsolationLevel? isolationLevel);
- void AbortTransaction();
- void CompleteTransaction();
- object Insert(string tableName, string primaryKeyName, bool autoIncrement, object poco);
- object Insert(string tableName, string primaryKeyName, object poco);
- object Insert(object poco);
- int Update(string tableName, string primaryKeyName, object poco, object primaryKeyValue);
- int Update(string tableName, string primaryKeyName, object poco);
- int Update(string tableName, string primaryKeyName, object poco, object primaryKeyValue, IEnumerable<string> columns);
- int Update(string tableName, string primaryKeyName, object poco, IEnumerable<string> columns);
- int Update(object poco, IEnumerable<string> columns);
- int Update(object poco, object primaryKeyValue, IEnumerable<string> columns);
- int Update(object poco);
- int Update(object poco, object primaryKeyValue);
- int Update<T>(string sql, params object[] args);
- int Update<T>(Sql sql);
- int Delete(string tableName, string primaryKeyName, object poco);
- int Delete(string tableName, string primaryKeyName, object poco, object primaryKeyValue);
- int Delete(object poco);
- int Delete<T>(string sql, params object[] args);
- int Delete<T>(Sql sql);
- int Delete<T>(object pocoOrPrimaryKey);
- void Save(string tableName, string primaryKeyName, object poco);
- void Save(object poco);
- }
-
- // Database class ... this is where most of the action happens
- public class Database : IDisposable, IDatabase
- {
- public const string MsSqlClientProvider = "System.Data.SqlClient";
-
- public Database(IDbConnection connection) : this(connection, DBType.NotSet) {}
-
- public Database(IDbConnection connection, DBType dbType)
- {
- _sharedConnection = connection;
- _connectionString = connection.ConnectionString;
- _sharedConnectionDepth = 2; // Prevent closing external connection
- _dbType = dbType;
- CommonConstruct();
- }
-
- public Database(string connectionString, string providerName)
- {
- _connectionString = connectionString;
- _providerName = providerName;
- CommonConstruct();
- }
-
- public Database(string connectionString, DbProviderFactory provider)
- {
- _connectionString = connectionString;
- _factory = provider;
- CommonConstruct();
- }
-
- public Database(string connectionStringName)
- {
- // Use first?
- if (connectionStringName == "")
- connectionStringName = ConfigurationManager.ConnectionStrings[0].Name;
-
- // Work out connection string and provider name
- var providerName = "System.Data.SqlClient";
- if (ConfigurationManager.ConnectionStrings[connectionStringName] != null)
- {
- if (!string.IsNullOrEmpty(ConfigurationManager.ConnectionStrings[connectionStringName].ProviderName))
- providerName = ConfigurationManager.ConnectionStrings[connectionStringName].ProviderName;
- }
- else
- {
- throw new InvalidOperationException("Can't find a connection string with the name '" + connectionStringName + "'");
- }
-
- // Store factory and connection string
- _connectionString = ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString;
- _providerName = providerName;
- CommonConstruct();
- }
-
- public enum DBType
- {
- NotSet,
- SqlServer,
- SqlServerCE,
- MySql,
- PostgreSQL,
- Oracle,
- SQLite
- }
-
- protected DBType _dbType = DBType.NotSet;
-
- // Common initialization
- private void CommonConstruct()
- {
- _transactionDepth = 0;
- ForceDateTimesToUtc = true;
- EnableAutoSelect = true;
-
- if (_providerName != null)
- _factory = DbProviderFactories.GetFactory(_providerName);
-
- if (_dbType == DBType.NotSet)
- {
- _dbType = DBType.SqlServer;
- string dbtype = (_factory == null ? _sharedConnection.GetType() : _factory.GetType()).Name;
- if (dbtype.StartsWith("MySql"))
- _dbType = DBType.MySql;
- else if (dbtype.StartsWith("SqlCe"))
- _dbType = DBType.SqlServerCE;
- else if (dbtype.StartsWith("Npgsql"))
- _dbType = DBType.PostgreSQL;
- else if (dbtype.StartsWith("Oracle"))
- _dbType = DBType.Oracle;
- else if (dbtype.StartsWith("SQLite"))
- _dbType = DBType.SQLite;
- }
-
- if (_dbType == DBType.MySql && _connectionString != null && _connectionString.IndexOf("Allow User Variables=true") >= 0)
- _paramPrefix = "?";
- if (_dbType == DBType.Oracle)
- _paramPrefix = ":";
- }
-
- // Automatically close one open shared connection
- public void Dispose()
- {
- // Automatically close one open connection reference
- // (Works with KeepConnectionAlive and manually opening a shared connection)
- CloseSharedConnection();
- }
-
- // Set to true to keep the first opened connection alive until this object is disposed
- public bool KeepConnectionAlive { get; set; }
-
- // Open a connection (can be nested)
- public void OpenSharedConnection()
- {
- if (_sharedConnectionDepth == 0)
- {
- _sharedConnection = _factory.CreateConnection();
- _sharedConnection.ConnectionString = _connectionString;
-
- if (_sharedConnection.State == ConnectionState.Broken)
- _sharedConnection.Close();
- if (_sharedConnection.State == ConnectionState.Closed)
- _sharedConnection.Open();
-
- _sharedConnection = OnConnectionOpened(_sharedConnection);
-
- if (KeepConnectionAlive)
- _sharedConnectionDepth++; // Make sure you call Dispose
- }
- _sharedConnectionDepth++;
- }
-
- // Close a previously opened connection
- public void CloseSharedConnection()
- {
- if (_sharedConnectionDepth > 0)
- {
- _sharedConnectionDepth--;
- if (_sharedConnectionDepth == 0)
- {
- OnConnectionClosing(_sharedConnection);
- _sharedConnection.Dispose();
- _sharedConnection = null;
- }
- }
- }
-
- public VersionExceptionHandling VersionException
- {
- get { return _versionException; }
- set { _versionException = value; }
- }
-
- // Access to our shared connection
- public IDbConnection Connection
- {
- get { return _sharedConnection; }
- }
-
- public IDbTransaction Transaction
- {
- get { return _transaction; }
- }
-
- public IDataParameter CreateParameter()
- {
- using (var conn = _sharedConnection ?? _factory.CreateConnection())
- using (var comm = conn.CreateCommand())
- return comm.CreateParameter();
- }
-
- // Helper to create a transaction scope
- public Transaction GetTransaction()
- {
- return GetTransaction(null);
- }
-
- public Transaction GetTransaction(IsolationLevel? isolationLevel)
- {
- return new Transaction(this, isolationLevel);
- }
-
- // Use by derived repo generated by T4 templates
- public virtual void OnBeginTransaction() { }
- public virtual void OnEndTransaction() { }
-
- public void BeginTransaction()
- {
- BeginTransaction(null);
- }
-
- // Start a new transaction, can be nested, every call must be
- // matched by a call to AbortTransaction or CompleteTransaction
- // Use `using (var scope=db.Transaction) { scope.Complete(); }` to ensure correct semantics
- public void BeginTransaction(IsolationLevel? isolationLevel)
- {
- _transactionDepth++;
-
- if (_transactionDepth == 1)
- {
- OpenSharedConnection();
- _transaction = isolationLevel == null ? _sharedConnection.BeginTransaction() : _sharedConnection.BeginTransaction(isolationLevel.Value);
- _transactionCancelled = false;
- OnBeginTransaction();
- }
-
- }
-
- // Internal helper to cleanup transaction stuff
- void CleanupTransaction()
- {
- OnEndTransaction();
-
- if (_transactionCancelled)
- _transaction.Rollback();
- else
- _transaction.Commit();
-
- _transaction.Dispose();
- _transaction = null;
-
- CloseSharedConnection();
- }
-
- // Abort the entire outer most transaction scope
- public void AbortTransaction()
- {
- _transactionCancelled = true;
- if ((--_transactionDepth) == 0)
- CleanupTransaction();
- }
-
- // Complete the transaction
- public void CompleteTransaction()
- {
- if ((--_transactionDepth) == 0)
- CleanupTransaction();
- }
-
- // Helper to handle named parameters from object properties
- static Regex rxParams = new Regex(@"(?<!@)@\w+", RegexOptions.Compiled);
- public static string ProcessParams(string _sql, object[] args_src, List<object> args_dest)
- {
- return rxParams.Replace(_sql, m =>
- {
- string param = m.Value.Substring(1);
-
- object arg_val;
-
- int paramIndex;
- if (int.TryParse(param, out paramIndex))
- {
- // Numbered parameter
- if (paramIndex < 0 || paramIndex >= args_src.Length)
- throw new ArgumentOutOfRangeException(string.Format("Parameter '@{0}' specified but only {1} parameters supplied (in `{2}`)", paramIndex, args_src.Length, _sql));
- arg_val = args_src[paramIndex];
- }
- else
- {
- // Look for a property on one of the arguments with this name
- bool found = false;
- arg_val = null;
- foreach (var o in args_src)
- {
- var pi = o.GetType().GetProperty(param);
- if (pi != null)
- {
- arg_val = pi.GetValue(o, null);
- found = true;
- break;
- }
- }
-
- if (!found)
- throw new ArgumentException(string.Format("Parameter '@{0}' specified but none of the passed arguments have a property with this name (in '{1}')", param, _sql));
- }
-
- // Expand collections to parameter lists
- if ((arg_val as System.Collections.IEnumerable) != null &&
- (arg_val as string) == null &&
- (arg_val as byte[]) == null)
- {
- var sb = new StringBuilder();
- foreach (var i in arg_val as System.Collections.IEnumerable)
- {
- var indexOfExistingValue = args_dest.IndexOf(i);
- if (indexOfExistingValue >= 0)
- {
- sb.Append((sb.Length == 0 ? "@" : ",@") + indexOfExistingValue);
- }
- else
- {
- sb.Append((sb.Length == 0 ? "@" : ",@") + args_dest.Count);
- args_dest.Add(i);
- }
- }
- if (sb.Length == 0)
- {
- sb.AppendFormat("select 1 /*peta_dual*/ where 1 = 0");
- }
- return sb.ToString();
- }
- else
- {
- var indexOfExistingValue = args_dest.IndexOf(arg_val);
- if (indexOfExistingValue >= 0)
- return "@" + indexOfExistingValue;
-
- args_dest.Add(arg_val);
- return "@" + (args_dest.Count - 1).ToString();
- }
- }
- );
- }
-
- // Add a parameter to a DB command
- void AddParam(IDbCommand cmd, object item, string ParameterPrefix)
- {
- // Convert value to from poco type to db type
- if (Database.Mapper != null && item!=null)
- {
- var fn = Database.Mapper.GetToDbConverter(item.GetType());
- if (fn!=null)
- item = fn(item);
- }
-
- // Support passed in parameters
- var idbParam = item as IDbDataParameter;
- if (idbParam != null)
- {
- idbParam.ParameterName = string.Format("{0}{1}", ParameterPrefix, cmd.Parameters.Count);
- cmd.Parameters.Add(idbParam);
- return;
- }
- var p = cmd.CreateParameter();
- p.ParameterName = string.Format("{0}{1}", ParameterPrefix, cmd.Parameters.Count);
-
- if (item == null)
- {
- p.Value = DBNull.Value;
- }
- else
- {
- var t = item.GetType();
- if (t.IsEnum) // PostgreSQL .NET driver wont cast enum to int
- {
- p.Value = (int)item;
- }
- else if (t == typeof(Guid))
- {
- p.Value = item.ToString();
- p.DbType = DbType.String;
- p.Size = 40;
- }
- else if (t == typeof(string))
- {
- p.Size = Math.Max((item as string).Length + 1, 4000); // Help query plan caching by using common size
- p.Value = item;
- }
- else if (t == typeof(AnsiString))
- {
- // Thanks @DataChomp for pointing out the SQL Server indexing performance hit of using wrong string type on varchar
- p.Size = Math.Max((item as AnsiString).Value.Length + 1, 4000);
- p.Value = (item as AnsiString).Value;
- p.DbType = DbType.AnsiString;
- }
- else if (t == typeof(bool) && _dbType != DBType.PostgreSQL)
- {
- p.Value = ((bool)item) ? 1 : 0;
- }
- else if (item.GetType().Name == "SqlGeography") //SqlGeography is a CLR Type
- {
- p.GetType().GetProperty("UdtTypeName").SetValue(p, "geography", null); //geography is the equivalent SQL Server Type
- p.Value = item;
- }
-
- else if (item.GetType().Name == "SqlGeometry") //SqlGeometry is a CLR Type
- {
- p.GetType().GetProperty("UdtTypeName").SetValue(p, "geometry", null); //geography is the equivalent SQL Server Type
- p.Value = item;
- }
- else
- {
- p.Value = item;
- }
- }
-
- cmd.Parameters.Add(p);
- }
-
- // Create a command
- static Regex rxParamsPrefix = new Regex(@"(?<!@)@\w+", RegexOptions.Compiled);
- IDbCommand CreateCommand(IDbConnection connection, string sql, params object[] args)
- {
- // Perform parameter prefix replacements
- if (_paramPrefix != "@")
- sql = rxParamsPrefix.Replace(sql, m => _paramPrefix + m.Value.Substring(1));
- sql = sql.Replace("@@", "@"); // <- double @@ escapes a single @
-
- // Create the command and add parameters
- IDbCommand cmd = connection.CreateCommand();
- cmd.Connection = connection;
- cmd.CommandText = sql;
- cmd.Transaction = _transaction;
-
- foreach (var item in args)
- {
- AddParam(cmd, item, _paramPrefix);
- }
-
- if (_dbType == DBType.Oracle)
- cmd.GetType().GetProperty("BindByName").SetValue(cmd, true, null);
-
- if (_dbType == DBType.Oracle || _dbType == DBType.MySql)
- cmd.CommandText = cmd.CommandText.Replace("/*peta_dual*/", "from dual");
-
- if (!String.IsNullOrEmpty(sql))
- DoPreExecute(cmd);
-
- return cmd;
- }
-
- // Override this to log/capture exceptions
- public virtual void OnException(Exception x)
- {
- System.Diagnostics.Debug.WriteLine(x.ToString());
- System.Diagnostics.Debug.WriteLine(LastCommand);
- }
-
- // Override this to log commands, or modify command before execution
- public virtual IDbConnection OnConnectionOpened(IDbConnection conn) { return conn; }
- public virtual void OnConnectionClosing(IDbConnection conn) { }
- public virtual void OnExecutingCommand(IDbCommand cmd) { }
- public virtual void OnExecutedCommand(IDbCommand cmd) { }
-
- // Execute a non-query command
- public int Execute(string sql, params object[] args)
- {
- return Execute(new Sql(sql, args));
- }
-
- public int Execute(Sql Sql)
- {
- var sql = Sql.SQL;
- var args = Sql.Arguments;
-
- try
- {
- OpenSharedConnection();
- try
- {
- using (var cmd = CreateCommand(_sharedConnection, sql, args))
- {
- var result = cmd.ExecuteNonQuery();
- OnExecutedCommand(cmd);
- return result;
- }
- }
- finally
- {
- CloseSharedConnection();
- }
- }
- catch (Exception x)
- {
- OnException(x);
- throw;
- }
- }
-
- // Execute and cast a scalar property
- public T ExecuteScalar<T>(string sql, params object[] args)
- {
- return ExecuteScalar<T>(new Sql(sql, args));
- }
-
- public T ExecuteScalar<T>(Sql Sql)
- {
- var sql = Sql.SQL;
- var args = Sql.Arguments;
-
- try
- {
- OpenSharedConnection();
- try
- {
- using (var cmd = CreateCommand(_sharedConnection, sql, args))
- {
- object val = cmd.ExecuteScalar();
- OnExecutedCommand(cmd);
-
- Type t = typeof(T);
- Type u = Nullable.GetUnderlyingType(t);
-
- if (val == null || val == DBNull.Value)
- return default(T);
-
- return u != null ? (T) Convert.ChangeType(val, u) : (T) Convert.ChangeType(val, t);
- }
- }
- finally
- {
- CloseSharedConnection();
- }
- }
- catch (Exception x)
- {
- OnException(x);
- throw;
- }
- }
-
- static Regex rxSelect = new Regex(@"\A\s*(SELECT|EXECUTE|CALL|EXEC)\s", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Multiline);
- static Regex rxFrom = new Regex(@"\A\s*FROM\s", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Multiline);
- string AddSelectClause<T>(string sql)
- {
- if (sql.StartsWith(";"))
- return sql.Substring(1);
-
- if (!rxSelect.IsMatch(sql))
- {
- var pd = PocoData.ForType(typeof(T));
- var tableName = EscapeTableName(pd.TableInfo.TableName);
- string cols = string.Join(", ", (from c in pd.QueryColumns select EscapeSqlIdentifier(c)).ToArray());
- if (!rxFrom.IsMatch(sql))
- sql = string.Format("SELECT {0} FROM {1} {2}", cols, tableName, sql);
- else
- sql = string.Format("SELECT {0} {1}", cols, sql);
- }
- return sql;
- }
-
- public bool ForceDateTimesToUtc { get; set; }
- public bool EnableAutoSelect { get; set; }
-
- // Return a typed list of pocos
- public List<T> Fetch<T>(string sql, params object[] args)
- {
- return Fetch<T>(new Sql(sql, args));
- }
-
- public List<T> Fetch<T>(Sql sql)
- {
- return Query<T>(sql).ToList();
- }
-
- public List<T> Fetch<T>()
- {
- return Fetch<T>("");
- }
-
- static Regex rxColumns = new Regex(@"\A\s*SELECT\s+((?:\((?>\((?<depth>)|\)(?<-depth>)|.?)*(?(depth)(?!))\)|.)*?)(?<!,\s+)\bFROM\b", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled);
- static Regex rxOrderBy = new Regex(@"\bORDER\s+BY\s+(?!.*?(?:\)|\s+)AS\s)(?:\((?>\((?<depth>)|\)(?<-depth>)|.?)*(?(depth)(?!))\)|[\w\(\)\.])+(?:\s+(?:ASC|DESC))?(?:\s*,\s*(?:\((?>\((?<depth>)|\)(?<-depth>)|.?)*(?(depth)(?!))\)|[\w\(\)\.])+(?:\s+(?:ASC|DESC))?)*", RegexOptions.RightToLeft | RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled);
- static Regex rxDistinct = new Regex(@"\ADISTINCT\s", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled);
- public static bool SplitSqlForPaging(string sql, out string sqlCount, out string sqlSelectRemoved, out string sqlOrderBy)
- {
- sqlSelectRemoved = null;
- sqlCount = null;
- sqlOrderBy = null;
-
- // Extract the columns from "SELECT <whatever> FROM"
- var m = rxColumns.Match(sql);
- if (!m.Success)
- return false;
-
- // Save column list and replace with COUNT(*)
- Group g = m.Groups[1];
- sqlSelectRemoved = sql.Substring(g.Index);
-
- if (rxDistinct.IsMatch(sqlSelectRemoved))
- sqlCount = sql.Substring(0, g.Index) + "COUNT(" + m.Groups[1].ToString().Trim() + ") " + sql.Substring(g.Index + g.Length);
- else
- sqlCount = sql.Substring(0, g.Index) + "COUNT(*) " + sql.Substring(g.Index + g.Length);
-
- // Look for an "ORDER BY <whatever>" clause
- m = rxOrderBy.Match(sqlCount);
- if (m.Success)
- {
- g = m.Groups[0];
- sqlOrderBy = g.ToString();
- sqlCount = sqlCount.Substring(0, g.Index) + sqlCount.Substring(g.Index + g.Length);
- }
-
- return true;
- }
-
- public void BuildPageQueries<T>(long skip, long take, string sql, ref object[] args, out string sqlCount, out string sqlPage)
- {
- // Add auto select clause
- sql=AddSelectClause<T>(sql);
-
- // Split the SQL into the bits we need
- string sqlSelectRemoved, sqlOrderBy;
- if (!SplitSqlForPaging(sql, out sqlCount, out sqlSelectRemoved, out sqlOrderBy))
- throw new Exception("Unable to parse SQL statement for paged query");
- if (_dbType == DBType.Oracle && sqlSelectRemoved.StartsWith("*"))
- throw new Exception("Query must alias '*' when performing a paged query.\neg. select t.* from table t order by t.id");
-
- // Build the SQL for the actual final result
- if (_dbType == DBType.SqlServer || _dbType == DBType.Oracle)
- {
- sqlSelectRemoved = rxOrderBy.Replace(sqlSelectRemoved, "");
- if (rxDistinct.IsMatch(sqlSelectRemoved))
- {
- sqlSelectRemoved = "peta_inner.* FROM (SELECT " + sqlSelectRemoved + ") peta_inner";
- }
- sqlPage = string.Format("SELECT * FROM (SELECT ROW_NUMBER() OVER ({0}) peta_rn, {1}) peta_paged WHERE peta_rn>@{2} AND peta_rn<=@{3}",
- sqlOrderBy==null ? "ORDER BY (SELECT NULL /*peta_dual*/)" : sqlOrderBy, sqlSelectRemoved, args.Length, args.Length + 1);
- args = args.Concat(new object[] { skip, skip+take }).ToArray();
- }
- else if (_dbType == DBType.SqlServerCE)
- {
- sqlPage = string.Format("{0}\nOFFSET @{1} ROWS FETCH NEXT @{2} ROWS ONLY", sql, args.Length, args.Length + 1);
- args = args.Concat(new object[] { skip, take }).ToArray();
- }
- else
- {
- sqlPage = string.Format("{0}\nLIMIT @{1} OFFSET @{2}", sql, args.Length, args.Length + 1);
- args = args.Concat(new object[] { take, skip }).ToArray();
- }
-
- }
-
- // Fetch a page
- public Page<T> Page<T>(long page, long itemsPerPage, string sql, params object[] args)
- {
- string sqlCount, sqlPage;
- BuildPageQueries<T>((page-1)*itemsPerPage, itemsPerPage, sql, ref args, out sqlCount, out sqlPage);
-
- // Save the one-time command time out and use it for both queries
- int saveTimeout = OneTimeCommandTimeout;
-
- // Setup the paged result
- var result = new Page<T>();
- result.CurrentPage = page;
- result.ItemsPerPage = itemsPerPage;
- result.TotalItems = ExecuteScalar<long>(sqlCount, args);
- result.TotalPages = result.TotalItems / itemsPerPage;
- if ((result.TotalItems % itemsPerPage) != 0)
- result.TotalPages++;
-
- OneTimeCommandTimeout = saveTimeout;
-
- // Get the records
- result.Items = Fetch<T>(sqlPage, args);
-
- // Done
- return result;
- }
-
- public Page<T> Page<T>(long page, long itemsPerPage, Sql sql)
- {
- return Page<T>(page, itemsPerPage, sql.SQL, sql.Arguments);
- }
-
- public List<T> Fetch<T>(long page, long itemsPerPage, string sql, params object[] args)
- {
- return SkipTake<T>((page - 1) * itemsPerPage, itemsPerPage, sql, args);
- }
-
- public List<T> Fetch<T>(long page, long itemsPerPage, Sql sql)
- {
- return SkipTake<T>((page - 1) * itemsPerPage, itemsPerPage, sql.SQL, sql.Arguments);
- }
-
- public List<T> SkipTake<T>(long skip, long take, string sql, params object[] args)
- {
- string sqlCount, sqlPage;
- BuildPageQueries<T>(skip, take, sql, ref args, out sqlCount, out sqlPage);
- return Fetch<T>(sqlPage, args);
- }
-
- public List<T> SkipTake<T>(long skip, long take, Sql sql)
- {
- return SkipTake<T>(skip, take, sql.SQL, sql.Arguments);
- }
-
- public Dictionary<TKey, TValue> Dictionary<TKey, TValue>(Sql Sql)
- {
- return Dictionary<TKey, TValue>(Sql.SQL, Sql.Arguments);
- }
-
- public Dictionary<TKey, TValue> Dictionary<TKey, TValue>(string sql, params object[] args)
- {
- var newDict = new Dictionary<TKey, TValue>();
- bool isConverterSet = false;
- Func<object, object> converter1 = x => x, converter2 = x => x;
-
- foreach (var line in Query<Dictionary<string, object>>(sql, args))
- {
- object key = line.ElementAt(0).Value;
- object value = line.ElementAt(1).Value;
-
- if (isConverterSet == false)
- {
- converter1 = PocoData.GetConverter(ForceDateTimesToUtc, null, typeof (TKey), key.GetType()) ?? (x => x);
- converter2 = PocoData.GetConverter(ForceDateTimesToUtc, null, typeof (TValue), value.GetType()) ?? (x => x);
- isConverterSet = true;
- }
-
- var keyConverted = (TKey) Convert.ChangeType(converter1(key), typeof (TKey));
-
- var valueType = Nullable.GetUnderlyingType(typeof (TValue)) ?? typeof (TValue);
- var valConv = converter2(value);
- var valConverted = valConv != null ? (TValue)Convert.ChangeType(valConv, valueType) : default(TValue);
-
- if (keyConverted != null)
- {
- newDict.Add(keyConverted, valConverted);
- }
- }
- return newDict;
- }
-
- // Return an enumerable collection of pocos
- public IEnumerable<T> Query<T>(string sql, params object[] args)
- {
- return Query<T>(new Sql(sql, args));
- }
-
- public IEnumerable<T> Query<T>(Sql Sql)
- {
- return Query<T>(default(T), Sql);
- }
-
- private IEnumerable<T> Query<T>(T instance, Sql Sql)
- {
- var sql = Sql.SQL;
- var args = Sql.Arguments;
-
- if (EnableAutoSelect)
- sql = AddSelectClause<T>(sql);
-
- OpenSharedConnection();
- try
- {
- using (var cmd = CreateCommand(_sharedConnection, sql, args))
- {
- IDataReader r;
- var pd = PocoData.ForType(typeof(T));
- try
- {
- r = cmd.ExecuteReader();
- OnExecutedCommand(cmd);
- }
- catch (Exception x)
- {
- OnException(x);
- throw;
- }
-
- using (r)
- {
- var factory = pd.GetFactory(cmd.CommandText, _sharedConnection.ConnectionString, ForceDateTimesToUtc, 0, r.FieldCount, r, instance) as Func<IDataReader, T, T>;
- while (true)
- {
- T poco;
- try
- {
- if (!r.Read())
- yield break;
- poco = factory(r, instance);
- }
- catch (Exception x)
- {
- OnException(x);
- throw;
- }
-
- yield return poco;
- }
- }
- }
- }
- finally
- {
- CloseSharedConnection();
- }
- }
-
- // Multi Fetch
- public List<TRet> Fetch<T1, T2, TRet>(Func<T1, T2, TRet> cb, string sql, params object[] args) { return Query<T1, T2, TRet>(cb, sql, args).ToList(); }
- public List<TRet> Fetch<T1, T2, T3, TRet>(Func<T1, T2, T3, TRet> cb, string sql, params object[] args) { return Query<T1, T2, T3, TRet>(cb, sql, args).ToList(); }
- public List<TRet> Fetch<T1, T2, T3, T4, TRet>(Func<T1, T2, T3, T4, TRet> cb, string sql, params object[] args) { return Query<T1, T2, T3, T4, TRet>(cb, sql, args).ToList(); }
-
- // Multi Query
- public IEnumerable<TRet> Query<T1, T2, TRet>(Func<T1, T2, TRet> cb, string sql, params object[] args) { return Query<TRet>(new Type[] { typeof(T1), typeof(T2) }, cb, new Sql(sql, args)); }
- public IEnumerable<TRet> Query<T1, T2, T3, TRet>(Func<T1, T2, T3, TRet> cb, string sql, params object[] args) { return Query<TRet>(new Type[] { typeof(T1), typeof(T2), typeof(T3) }, cb, new Sql(sql, args)); }
- public IEnumerable<TRet> Query<T1, T2, T3, T4, TRet>(Func<T1, T2, T3, T4, TRet> cb, string sql, params object[] args) { return Query<TRet>(new Type[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4) }, cb, new Sql(sql, args)); }
-
- // Multi Fetch (SQL builder)
- public List<TRet> Fetch<T1, T2, TRet>(Func<T1, T2, TRet> cb, Sql sql) { return Query<T1, T2, TRet>(cb, sql).ToList(); }
- public List<TRet> Fetch<T1, T2, T3, TRet>(Func<T1, T2, T3, TRet> cb, Sql sql) { return Query<T1, T2, T3, TRet>(cb, sql).ToList(); }
- public List<TRet> Fetch<T1, T2, T3, T4, TRet>(Func<T1, T2, T3, T4, TRet> cb, Sql sql) { return Query<T1, T2, T3, T4, TRet>(cb, sql).ToList(); }
-
- // Multi Query (SQL builder)
- public IEnumerable<TRet> Query<T1, T2, TRet>(Func<T1, T2, TRet> cb, Sql sql) { return Query<TRet>(new Type[] { typeof(T1), typeof(T2) }, cb, sql); }
- public IEnumerable<TRet> Query<T1, T2, T3, TRet>(Func<T1, T2, T3, TRet> cb, Sql sql) { return Query<TRet>(new Type[] { typeof(T1), typeof(T2), typeof(T3) }, cb, sql); }
- public IEnumerable<TRet> Query<T1, T2, T3, T4, TRet>(Func<T1, T2, T3, T4, TRet> cb, Sql sql) { return Query<TRet>(new Type[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4) }, cb, sql); }
-
- // Multi Fetch (Simple)
- public List<T1> Fetch<T1, T2>(string sql, params object[] args) { return Query<T1, T2>(sql, args).ToList(); }
- public List<T1> Fetch<T1, T2, T3>(string sql, params object[] args) { return Query<T1, T2, T3>(sql, args).ToList(); }
- public List<T1> Fetch<T1, T2, T3, T4>(string sql, params object[] args) { return Query<T1, T2, T3, T4>(sql, args).ToList(); }
-
- // Multi Query (Simple)
- public IEnumerable<T1> Query<T1, T2>(string sql, params object[] args) { return Query<T1>(new Type[] { typeof(T1), typeof(T2) }, null, new Sql(sql, args)); }
- public IEnumerable<T1> Query<T1, T2, T3>(string sql, params object[] args) { return Query<T1>(new Type[] { typeof(T1), typeof(T2), typeof(T3) }, null, new Sql(sql, args)); }
- public IEnumerable<T1> Query<T1, T2, T3, T4>(string sql, params object[] args) { return Query<T1>(new Type[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4) }, null, new Sql(sql, args)); }
-
- // Multi Fetch (Simple) (SQL builder)
- public List<T1> Fetch<T1, T2>(Sql sql) { return Query<T1, T2>(sql).ToList(); }
- public List<T1> Fetch<T1, T2, T3>(Sql sql) { return Query<T1, T2, T3>(sql).ToList(); }
- public List<T1> Fetch<T1, T2, T3, T4>(Sql sql) { return Query<T1, T2, T3, T4>(sql).ToList(); }
-
- // Multi Query (Simple) (SQL builder)
- public IEnumerable<T1> Query<T1, T2>(Sql sql) { return Query<T1>(new Type[] { typeof(T1), typeof(T2) }, null, sql); }
- public IEnumerable<T1> Query<T1, T2, T3>(Sql sql) { return Query<T1>(new Type[] { typeof(T1), typeof(T2), typeof(T3) }, null, sql); }
- public IEnumerable<T1> Query<T1, T2, T3, T4>(Sql sql) { return Query<T1>(new Type[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4) }, null, sql); }
-
- // Automagically guess the property relationships between various POCOs and create a delegate that will set them up
- object GetAutoMapper(Type[] types)
- {
- // Build a key
- var kb = new StringBuilder();
- foreach (var t in types)
- {
- kb.Append(t.ToString());
- kb.Append(":");
- }
- var key = kb.ToString();
-
- // Check cache
- RWLock.EnterReadLock();
- try
- {
- object mapper;
- if (AutoMappers.TryGetValue(key, out mapper))
- return mapper;
- }
- finally
- {
- RWLock.ExitReadLock();
- }
-
- // Create it
- RWLock.EnterWriteLock();
- try
- {
- // Try again
- object mapper;
- if (AutoMappers.TryGetValue(key, out mapper))
- return mapper;
-
- // Create a method
- var m = new DynamicMethod("petapoco_automapper", types[0], types, true);
- var il = m.GetILGenerator();
-
- for (int i = 1; i < types.Length; i++)
- {
- bool handled = false;
- for (int j = i - 1; j >= 0; j--)
- {
- // Find the property
- var candidates = from p in types[j].GetProperties() where p.PropertyType == types[i] select p;
- if (candidates.Count() == 0)
- continue;
- if (candidates.Count() > 1)
- throw new InvalidOperationException(string.Format("Can't auto join {0} as {1} has more than one property of type {0}", types[i], types[j]));
-
- // Generate code
- il.Emit(OpCodes.Ldarg_S, j);
- il.Emit(OpCodes.Ldarg_S, i);
- il.Emit(OpCodes.Callvirt, candidates.First().GetSetMethod(true));
- handled = true;
- }
-
- if (!handled)
- throw new InvalidOperationException(string.Format("Can't auto join {0}", types[i]));
- }
-
- il.Emit(OpCodes.Ldarg_0);
- il.Emit(OpCodes.Ret);
-
- // Cache it
- var del = m.CreateDelegate(Expression.GetFuncType(types.Concat(types.Take(1)).ToArray()));
- AutoMappers.Add(key, del);
- return del;
- }
- finally
- {
- RWLock.ExitWriteLock();
- }
- }
-
- // Find the split point in a result set for two different pocos and return the poco factory for the first
- Delegate FindSplitPoint(Type typeThis, Type typeNext, string sql, IDataReader r, ref int pos)
- {
- // Last?
- if (typeNext == null)
- return PocoData.ForType(typeThis).GetFactory(sql, _sharedConnection.ConnectionString, ForceDateTimesToUtc, pos, r.FieldCount - pos, r, null);
-
- // Get PocoData for the two types
- PocoData pdThis = PocoData.ForType(typeThis);
- PocoData pdNext = PocoData.ForType(typeNext);
-
- // Find split point
- int firstColumn = pos;
- var usedColumns = new Dictionary<string, bool>();
- for (; pos < r.FieldCount; pos++)
- {
- // Split if field name has already been used, or if the field doesn't exist in current poco but does in the next
- string fieldName = r.GetName(pos);
- if (usedColumns.ContainsKey(fieldName) || (!pdThis.Columns.ContainsKey(fieldName) && pdNext.Columns.ContainsKey(fieldName)))
- {
- return pdThis.GetFactory(sql, _sharedConnection.ConnectionString, ForceDateTimesToUtc, firstColumn, pos - firstColumn, r, null);
- }
- usedColumns.Add(fieldName, true);
- }
-
- throw new InvalidOperationException(string.Format("Couldn't find split point between {0} and {1}", typeThis, typeNext));
- }
-
- // Instance data used by the Multipoco factory delegate - essentially a list of the nested poco factories to call
- class MultiPocoFactory
- {
- public List<Delegate> m_Delegates;
- public Delegate GetItem(int index) { return m_Delegates[index]; }
- }
-
- // Create a multi-poco factory
- Func<IDataReader, object, TRet> CreateMultiPocoFactory<TRet>(Type[] types, string sql, IDataReader r)
- {
- var m = new DynamicMethod("petapoco_multipoco_factory", typeof(TRet), new Type[] { typeof(MultiPocoFactory), typeof(IDataReader), typeof(object) }, typeof(MultiPocoFactory));
- var il = m.GetILGenerator();
-
- // Load the callback
- il.Emit(OpCodes.Ldarg_2);
-
- // Call each delegate
- var dels = new List<Delegate>();
- int pos = 0;
- for (int i=0; i<types.Length; i++)
- {
- // Add to list of delegates to call
- var del = FindSplitPoint(types[i], i + 1 < types.Length ? types[i + 1] : null, sql, r, ref pos);
- dels.Add(del);
-
- // Get the delegate
- il.Emit(OpCodes.Ldarg_0); // callback,this
- il.Emit(OpCodes.Ldc_I4, i); // callback,this,Index
- il.Emit(OpCodes.Callvirt, typeof(MultiPocoFactory).GetMethod("GetItem")); // callback,Delegate
- il.Emit(OpCodes.Ldarg_1); // callback,delegate, datareader
- il.Emit(OpCodes.Ldnull); // callback,delegate, datareader,null
-
- // Call Invoke
- var tDelInvoke = del.GetType().GetMethod("Invoke");
- il.Emit(OpCodes.Callvirt, tDelInvoke); // Poco left on stack
- }
-
- // By now we should have the callback and the N pocos all on the stack. Call the callback and we're done
- il.Emit(OpCodes.Callvirt, Expression.GetFuncType(types.Concat(new Type[] { typeof(TRet) }).ToArray()).GetMethod("Invoke"));
- il.Emit(OpCodes.Ret);
-
- // Finish up
- return (Func<IDataReader, object, TRet>)m.CreateDelegate(typeof(Func<IDataReader, object, TRet>), new MultiPocoFactory() { m_Delegates = dels });
- }
-
- // Various cached stuff
- static Dictionary<string, object> MultiPocoFactories = new Dictionary<string, object>();
- static Dictionary<string, object> AutoMappers = new Dictionary<string, object>();
- static System.Threading.ReaderWriterLockSlim RWLock = new System.Threading.ReaderWriterLockSlim();
-
- // Get (or create) the multi-poco factory for a…
Large files files are truncated, but you can click here to view the full file