Updated PetaPoco with Exists, AddMany,UpdateMany,InsertMany,SaveMany

This commit is contained in:
kay.one 2011-06-16 20:36:52 -07:00
parent 3cccb5858a
commit dd55a055e6
2 changed files with 137 additions and 27 deletions

View File

@ -1,4 +1,6 @@
using System;
using System.Data;
using System.Data.Common;
using System.Data.SQLite;
using System.IO;
using MvcMiniProfiler.Data;
@ -58,9 +60,12 @@ namespace NzbDrone.Core.Datastore
public static IDatabase GetPetaPocoDb(string connectionString)
{
var profileConnection = ProfiledDbConnection.Get(new SQLiteConnection(connectionString));
PetaPoco.Database.Mapper = new CustomeMapper();
var db = new PetaPoco.Database(profileConnection);
db.OpenSharedConnection();
Database.Mapper = new CustomeMapper();
var db = new Database(profileConnection);
if (profileConnection.State != ConnectionState.Open)
profileConnection.Open();
return db;
}

View File

@ -1,5 +1,5 @@
/* PetaPoco v4.0.2 - A Tiny ORMish thing for your POCO's.
* Copyright © 2011 Topten Software. All Rights Reserved.
* Copyright © 2011 Topten Software. All Rights Reserved.
*
* Apache License 2.0 - http://www.toptensoftware.com/petapoco/license
*
@ -24,7 +24,6 @@ using System.Reflection.Emit;
using System.Linq.Expressions;
// ReSharper disable
namespace PetaPoco
{
// Poco's marked [Explicit] require all column properties to be marked
@ -182,6 +181,8 @@ namespace PetaPoco
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);
@ -221,13 +222,14 @@ namespace PetaPoco
T FirstOrDefault<T>(Sql sql);
bool Exists<T>(object primaryKey);
int OneTimeCommandTimeout { get; set; }
bool Exists<T>(string sql, params object[] args);
}
public interface IDatabase : IDatabaseQuery
{
void Dispose();
IDbConnection Connection { get; }
Transaction GetTransaction();
ITransaction GetTransaction();
void BeginTransaction();
void AbortTransaction();
void CompleteTransaction();
@ -240,6 +242,7 @@ namespace PetaPoco
int Update(object poco, object primaryKeyValue);
int Update<T>(string sql, params object[] args);
int Update<T>(Sql sql);
void UpdateMany<T>(IEnumerable<T> pocoList);
int Delete(string tableName, string primaryKeyName, object poco);
int Delete(string tableName, string primaryKeyName, object poco, object primaryKeyValue);
int Delete(object poco);
@ -248,6 +251,8 @@ namespace PetaPoco
int Delete<T>(object pocoOrPrimaryKey);
void Save(string tableName, string primaryKeyName, object poco);
void Save(object poco);
void InsertMany<T>(IEnumerable<T> pocoList);
void SaveMany<T>(IEnumerable<T> pocoList);
}
// Database class ... this is where most of the action happens
@ -353,19 +358,17 @@ namespace PetaPoco
{
_sharedConnection = _factory.CreateConnection();
_sharedConnection.ConnectionString = _connectionString;
_sharedConnection.Open();
if (KeepConnectionAlive)
_sharedConnectionDepth++; // Make sure you call Dispose
}
if (_sharedConnection.State != ConnectionState.Open)
_sharedConnection.Open();
_sharedConnectionDepth++;
}
// Close a previously opened connection
/// <summary>
/// Close a previously opened connection
/// </summary>
public void CloseSharedConnection()
{
if (_sharedConnectionDepth > 0)
@ -386,7 +389,7 @@ namespace PetaPoco
}
// Helper to create a transaction scope
public Transaction GetTransaction()
public ITransaction GetTransaction()
{
return new Transaction(this);
}
@ -750,7 +753,7 @@ namespace PetaPoco
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+(?:\((?>\((?<depth>)|\)(?<-depth>)|.?)*(?(depth)(?!))\)|[\w\(\)\.])+(?:\s+(?:ASC|DESC))?(?:\s*,\s*(?:\((?>\((?<depth>)|\)(?<-depth>)|.?)*(?(depth)(?!))\)|[\w\(\)\.])+(?:\s+(?:ASC|DESC))?)*", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled);
public static bool SplitSqlForPaging(string sql, out string sqlCount, out string sqlSelectRemoved, out string sqlOrderBy)
public static bool SplitSqlForPaging<T>(string sql, out string sqlCount, out string sqlSelectRemoved, out string sqlOrderBy)
{
sqlSelectRemoved = null;
sqlCount = null;
@ -766,26 +769,32 @@ namespace PetaPoco
sqlCount = sql.Substring(0, g.Index) + "COUNT(*) " + sql.Substring(g.Index + g.Length);
sqlSelectRemoved = sql.Substring(g.Index);
// Look for an "ORDER BY <whatever>" clause
// Look for an "ORDER BY <whatever>" clause or primarykey from pocodata
var data = PocoData.ForType(typeof(T));
m = rxOrderBy.Match(sqlCount);
if (!m.Success)
if (!m.Success
&& (string.IsNullOrEmpty(data.TableInfo.PrimaryKey) ||
(!data.TableInfo.PrimaryKey.Split(',').All(x => data.Columns.Values.Any(y => y.ColumnName.Equals(x, StringComparison.OrdinalIgnoreCase))))))
{
return false;
}
g = m.Groups[0];
sqlOrderBy = g.ToString();
sqlOrderBy = m.Success ? g.ToString() : "ORDER BY " + data.TableInfo.PrimaryKey;
sqlCount = sqlCount.Substring(0, g.Index) + sqlCount.Substring(g.Index + g.Length);
return true;
}
public void BuildPageQueries<T>(long page, long itemsPerPage, string sql, ref object[] args, out string sqlCount, out string sqlPage)
private 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))
if (!SplitSqlForPaging<T>(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");
@ -793,20 +802,21 @@ namespace PetaPoco
// Build the SQL for the actual final result
if (_dbType == DBType.SqlServer || _dbType == DBType.Oracle)
{
var fromIndex = sqlSelectRemoved.IndexOf("from", StringComparison.OrdinalIgnoreCase);
sqlSelectRemoved = rxOrderBy.Replace(sqlSelectRemoved, "");
sqlPage = string.Format("SELECT * FROM (SELECT ROW_NUMBER() OVER ({0}) peta_rn, {1}) peta_paged WHERE peta_rn>@{2} AND peta_rn<=@{3}",
sqlOrderBy, sqlSelectRemoved, args.Length, args.Length + 1);
args = args.Concat(new object[] { (page - 1) * itemsPerPage, page * itemsPerPage }).ToArray();
sqlPage = string.Format("SELECT * FROM (SELECT {2}, ROW_NUMBER() OVER ({0}) peta_rn {1}) peta_paged WHERE peta_rn>@{3} AND peta_rn<=@{4}",
sqlOrderBy, sqlSelectRemoved.Substring(fromIndex), sqlSelectRemoved.Substring(0, fromIndex - 1), 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[] { (page - 1) * itemsPerPage, itemsPerPage }).ToArray();
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[] { itemsPerPage, (page - 1) * itemsPerPage }).ToArray();
args = args.Concat(new object[] { take, skip }).ToArray();
}
}
@ -815,7 +825,11 @@ namespace PetaPoco
public Page<T> Page<T>(long page, long itemsPerPage, string sql, params object[] args)
{
string sqlCount, sqlPage;
BuildPageQueries<T>(page, itemsPerPage, sql, ref args, out sqlCount, out sqlPage);
long skip = (page - 1) * itemsPerPage;
long take = itemsPerPage;
BuildPageQueries<T>(skip, take, sql, ref args, out sqlCount, out sqlPage);
// Save the one-time command time out and use it for both queries
int saveTimeout = OneTimeCommandTimeout;
@ -843,6 +857,21 @@ namespace PetaPoco
return Page<T>(page, 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);
var result = Fetch<T>(sqlPage, args);
return result;
}
public List<T> SkipTake<T>(long skip, long take, Sql sql)
{
return SkipTake<T>(skip, take, sql.SQL, sql.Arguments);
}
// Return an enumerable collection of pocos
public IEnumerable<T> Query<T>(string sql, params object[] args)
{
@ -1213,6 +1242,38 @@ namespace PetaPoco
var primaryKeyValuePairs = GetPrimaryKeyValues(PocoData.ForType(typeof(T)).TableInfo.PrimaryKey, primaryKey);
return FirstOrDefault<T>(string.Format("WHERE {0}", BuildPrimaryKeySql(primaryKeyValuePairs, ref index)), primaryKeyValuePairs.Select(x => x.Value).ToArray()) != null;
}
public bool Exists<T>(string sql, params object[] args)
{
var poco = PocoData.ForType(typeof(T)).TableInfo;
string existsTemplate;
switch (_dbType)
{
case DBType.SQLite:
case DBType.MySql:
{
existsTemplate = "SELECT EXISTS (SELECT 1 FROM {0} WHERE {1})";
break;
}
case DBType.SqlServer:
{
existsTemplate = "IF EXISTS (SELECT 1 FROM {0} WHERE {1}) SELECT 1 ELSE SELECT 0";
break;
}
default:
{
existsTemplate = "SELECT COUNT(*) FROM {0} WHERE {1}";
break;
}
}
return ExecuteScalar<int>(string.Format(existsTemplate, poco.TableName, sql), args) != 0;
}
public T Single<T>(object primaryKey)
{
var index = 0;
@ -1297,7 +1358,6 @@ namespace PetaPoco
{
using (var cmd = CreateCommand(_sharedConnection, ""))
{
var pd = PocoData.ForObject(poco, primaryKeyName);
var names = new List<string>();
var values = new List<string>();
@ -1466,6 +1526,19 @@ namespace PetaPoco
return Insert(pd.TableInfo.TableName, pd.TableInfo.PrimaryKey, pd.TableInfo.AutoIncrement, poco);
}
public void InsertMany<T>(IEnumerable<T> pocoList)
{
using (var tran = GetTransaction())
{
foreach (var poco in pocoList)
{
Insert(poco);
}
tran.Complete();
}
}
// Update a record with values from a poco. primary key value can be either supplied or read from the poco
public int Update(string tableName, string primaryKeyName, object poco, object primaryKeyValue)
{
@ -1617,6 +1690,20 @@ namespace PetaPoco
return Execute(new Sql(string.Format("UPDATE {0}", EscapeTableName(pd.TableInfo.TableName))).Append(sql));
}
public void UpdateMany<T>(IEnumerable<T> pocoList)
{
using (var tran = GetTransaction())
{
foreach (var poco in pocoList)
{
Update(poco);
}
tran.Complete();
}
}
public int Delete(string tableName, string primaryKeyName, object poco)
{
return Delete(tableName, primaryKeyName, poco, null);
@ -1747,6 +1834,19 @@ namespace PetaPoco
Save(pd.TableInfo.TableName, pd.TableInfo.PrimaryKey, poco);
}
public void SaveMany<T>(IEnumerable<T> pocoList)
{
using (var tran = GetTransaction())
{
foreach (var poco in pocoList)
{
Save(poco);
}
tran.Complete();
}
}
public int CommandTimeout { get; set; }
public int OneTimeCommandTimeout { get; set; }
@ -2269,7 +2369,12 @@ namespace PetaPoco
}
// Transaction object helps maintain transaction depth counts
public class Transaction : IDisposable
public interface ITransaction : IDisposable
{
void Complete();
}
public class Transaction : ITransaction
{
public Transaction(Database db)
{