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

View File

@ -1,5 +1,5 @@
/* PetaPoco v4.0.2 - A Tiny ORMish thing for your POCO's. /* 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 * Apache License 2.0 - http://www.toptensoftware.com/petapoco/license
* *
@ -24,7 +24,6 @@ using System.Reflection.Emit;
using System.Linq.Expressions; using System.Linq.Expressions;
// ReSharper disable
namespace PetaPoco namespace PetaPoco
{ {
// Poco's marked [Explicit] require all column properties to be marked // 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); 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, string sql, params object[] args);
Page<T> Page<T>(long page, long itemsPerPage, Sql sql); 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, 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, 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); 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); T FirstOrDefault<T>(Sql sql);
bool Exists<T>(object primaryKey); bool Exists<T>(object primaryKey);
int OneTimeCommandTimeout { get; set; } int OneTimeCommandTimeout { get; set; }
bool Exists<T>(string sql, params object[] args);
} }
public interface IDatabase : IDatabaseQuery public interface IDatabase : IDatabaseQuery
{ {
void Dispose(); void Dispose();
IDbConnection Connection { get; } IDbConnection Connection { get; }
Transaction GetTransaction(); ITransaction GetTransaction();
void BeginTransaction(); void BeginTransaction();
void AbortTransaction(); void AbortTransaction();
void CompleteTransaction(); void CompleteTransaction();
@ -240,6 +242,7 @@ namespace PetaPoco
int Update(object poco, object primaryKeyValue); int Update(object poco, object primaryKeyValue);
int Update<T>(string sql, params object[] args); int Update<T>(string sql, params object[] args);
int Update<T>(Sql sql); 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);
int Delete(string tableName, string primaryKeyName, object poco, object primaryKeyValue); int Delete(string tableName, string primaryKeyName, object poco, object primaryKeyValue);
int Delete(object poco); int Delete(object poco);
@ -248,6 +251,8 @@ namespace PetaPoco
int Delete<T>(object pocoOrPrimaryKey); int Delete<T>(object pocoOrPrimaryKey);
void Save(string tableName, string primaryKeyName, object poco); void Save(string tableName, string primaryKeyName, object poco);
void Save(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 // Database class ... this is where most of the action happens
@ -353,19 +358,17 @@ namespace PetaPoco
{ {
_sharedConnection = _factory.CreateConnection(); _sharedConnection = _factory.CreateConnection();
_sharedConnection.ConnectionString = _connectionString; _sharedConnection.ConnectionString = _connectionString;
_sharedConnection.Open();
if (KeepConnectionAlive) if (KeepConnectionAlive)
_sharedConnectionDepth++; // Make sure you call Dispose _sharedConnectionDepth++; // Make sure you call Dispose
} }
if (_sharedConnection.State != ConnectionState.Open)
_sharedConnection.Open();
_sharedConnectionDepth++; _sharedConnectionDepth++;
} }
// Close a previously opened connection /// <summary>
/// Close a previously opened connection
/// </summary>
public void CloseSharedConnection() public void CloseSharedConnection()
{ {
if (_sharedConnectionDepth > 0) if (_sharedConnectionDepth > 0)
@ -386,7 +389,7 @@ namespace PetaPoco
} }
// Helper to create a transaction scope // Helper to create a transaction scope
public Transaction GetTransaction() public ITransaction GetTransaction()
{ {
return new Transaction(this); 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 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); 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; sqlSelectRemoved = null;
sqlCount = null; sqlCount = null;
@ -766,26 +769,32 @@ namespace PetaPoco
sqlCount = sql.Substring(0, g.Index) + "COUNT(*) " + sql.Substring(g.Index + g.Length); sqlCount = sql.Substring(0, g.Index) + "COUNT(*) " + sql.Substring(g.Index + g.Length);
sqlSelectRemoved = sql.Substring(g.Index); 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); 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; return false;
}
g = m.Groups[0]; 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); sqlCount = sqlCount.Substring(0, g.Index) + sqlCount.Substring(g.Index + g.Length);
return true; 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 // Add auto select clause
sql = AddSelectClause<T>(sql); sql = AddSelectClause<T>(sql);
// Split the SQL into the bits we need // Split the SQL into the bits we need
string sqlSelectRemoved, sqlOrderBy; 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"); throw new Exception("Unable to parse SQL statement for paged query");
if (_dbType == DBType.Oracle && sqlSelectRemoved.StartsWith("*")) 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"); 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 // Build the SQL for the actual final result
if (_dbType == DBType.SqlServer || _dbType == DBType.Oracle) if (_dbType == DBType.SqlServer || _dbType == DBType.Oracle)
{ {
var fromIndex = sqlSelectRemoved.IndexOf("from", StringComparison.OrdinalIgnoreCase);
sqlSelectRemoved = rxOrderBy.Replace(sqlSelectRemoved, ""); 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}", 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, args.Length, args.Length + 1); sqlOrderBy, sqlSelectRemoved.Substring(fromIndex), sqlSelectRemoved.Substring(0, fromIndex - 1), args.Length, args.Length + 1);
args = args.Concat(new object[] { (page - 1) * itemsPerPage, page * itemsPerPage }).ToArray(); args = args.Concat(new object[] { skip, skip + take }).ToArray();
} }
else if (_dbType == DBType.SqlServerCE) else if (_dbType == DBType.SqlServerCE)
{ {
sqlPage = string.Format("{0}\nOFFSET @{1} ROWS FETCH NEXT @{2} ROWS ONLY", sql, args.Length, args.Length + 1); 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 else
{ {
sqlPage = string.Format("{0}\nLIMIT @{1} OFFSET @{2}", sql, args.Length, args.Length + 1); 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) public Page<T> Page<T>(long page, long itemsPerPage, string sql, params object[] args)
{ {
string sqlCount, sqlPage; 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 // Save the one-time command time out and use it for both queries
int saveTimeout = OneTimeCommandTimeout; int saveTimeout = OneTimeCommandTimeout;
@ -843,6 +857,21 @@ namespace PetaPoco
return Page<T>(page, itemsPerPage, sql.SQL, sql.Arguments); 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 // Return an enumerable collection of pocos
public IEnumerable<T> Query<T>(string sql, params object[] args) 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); 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; 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) public T Single<T>(object primaryKey)
{ {
var index = 0; var index = 0;
@ -1297,7 +1358,6 @@ namespace PetaPoco
{ {
using (var cmd = CreateCommand(_sharedConnection, "")) using (var cmd = CreateCommand(_sharedConnection, ""))
{ {
var pd = PocoData.ForObject(poco, primaryKeyName); var pd = PocoData.ForObject(poco, primaryKeyName);
var names = new List<string>(); var names = new List<string>();
var values = 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); 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 // 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) 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)); 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) public int Delete(string tableName, string primaryKeyName, object poco)
{ {
return Delete(tableName, primaryKeyName, poco, null); return Delete(tableName, primaryKeyName, poco, null);
@ -1747,6 +1834,19 @@ namespace PetaPoco
Save(pd.TableInfo.TableName, pd.TableInfo.PrimaryKey, poco); 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 CommandTimeout { get; set; }
public int OneTimeCommandTimeout { get; set; } public int OneTimeCommandTimeout { get; set; }
@ -2269,7 +2369,12 @@ namespace PetaPoco
} }
// Transaction object helps maintain transaction depth counts // 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) public Transaction(Database db)
{ {