From 20b3408ac2be3e7b2c5ac836465c64354bd7382f Mon Sep 17 00:00:00 2001 From: Rodney Foley Date: Tue, 31 Jul 2012 17:47:45 -0700 Subject: [PATCH 1/5] Update Massive.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Minor cleanup Removed virtual from TableName and PrimaryKeyField properties Modify some usage to be more readable without any loss to number of lines Modified some usage to use Linq  Sync'd with primary existing coding standards in the file. Reduced total lines by 77 --- Massive.cs | 303 ++++++++++++++++++++--------------------------------- 1 file changed, 113 insertions(+), 190 deletions(-) diff --git a/Massive.cs b/Massive.cs index 9514810..9641f2a 100644 --- a/Massive.cs +++ b/Massive.cs @@ -10,16 +10,14 @@ using System.Threading.Tasks; using System.Data.SqlClient; using System.Text.RegularExpressions; - namespace Massive { public static class ObjectExtensions { /// /// Extension method for adding in a bunch of parameters /// public static void AddParams(this DbCommand cmd, params object[] args) { - foreach (var item in args) { + foreach (var item in args) AddParam(cmd, item); - } } /// /// Extension for adding single parameter @@ -30,16 +28,14 @@ public static class ObjectExtensions { if (item == null) { p.Value = DBNull.Value; } else { - if (item.GetType() == typeof(Guid)) { + if (item is Guid) { p.Value = item.ToString(); p.DbType = DbType.String; p.Size = 4000; - } else if (item.GetType() == typeof(ExpandoObject)) { + } else if (item is ExpandoObject) { var d = (IDictionary)item; p.Value = d.Values.FirstOrDefault(); - } else { - p.Value = item; - } + } else p.Value = item; if (item.GetType() == typeof(string)) p.Size = ((string)item).Length > 4000 ? -1 : 4000; } @@ -50,9 +46,8 @@ public static class ObjectExtensions { /// public static List ToExpandoList(this IDataReader rdr) { var result = new List(); - while (rdr.Read()) { + while (rdr.Read()) result.Add(rdr.RecordToExpando()); - } return result; } public static dynamic RecordToExpando(this IDataReader rdr) { @@ -68,19 +63,18 @@ public static class ObjectExtensions { public static dynamic ToExpando(this object o) { var result = new ExpandoObject(); var d = result as IDictionary; //work with the Expando as a Dictionary - if (o.GetType() == typeof(ExpandoObject)) return o; //shouldn't have to... but just in case - if (o.GetType() == typeof(NameValueCollection) || o.GetType().IsSubclassOf(typeof(NameValueCollection))) { + Type oType = o.GetType(); + if (oType == typeof(ExpandoObject)) return o; //shouldn't have to... but just in case + if (oType == typeof(NameValueCollection) || oType.IsSubclassOf(typeof(NameValueCollection))){ var nv = (NameValueCollection)o; - nv.Cast().Select(key => new KeyValuePair(key, nv[key])).ToList().ForEach(i => d.Add(i)); + nv.Cast().Select(key => new KeyValuePair(key, nv[key])).ToList().ForEach(d.Add); } else { - var props = o.GetType().GetProperties(); - foreach (var item in props) { + var props = oType.GetProperties(); + foreach (var item in props) d.Add(item.Name, item.GetValue(o, null)); - } } return result; } - /// /// Turns the object into a Dictionary /// @@ -88,7 +82,6 @@ public static class ObjectExtensions { return (IDictionary)thingy.ToExpando(); } } - /// /// Convenience class for opening/executing data /// @@ -101,8 +94,7 @@ public static class DB { throw new InvalidOperationException("Need a connection string name - can't determine what it is"); } } - } - + } /// /// A class that wraps your database table in Dynamic Funtime /// @@ -118,15 +110,12 @@ public class DynamicModel : DynamicObject { TableName = tableName == "" ? this.GetType().Name : tableName; PrimaryKeyField = string.IsNullOrEmpty(primaryKeyField) ? "ID" : primaryKeyField; DescriptorField = descriptorField; - var _providerName = "System.Data.SqlClient"; - + var _providerName = "System.Data.SqlClient"; if(ConfigurationManager.ConnectionStrings[connectionStringName].ProviderName != null) - _providerName = ConfigurationManager.ConnectionStrings[connectionStringName].ProviderName; - + _providerName = ConfigurationManager.ConnectionStrings[connectionStringName].ProviderName; _factory = DbProviderFactories.GetFactory(_providerName); ConnectionString = ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString; } - /// /// Creates a new Expando from a Form POST - white listed against the columns in the DB /// @@ -137,11 +126,11 @@ public class DynamicModel : DynamicObject { //loop the collection, setting only what's in the Schema foreach (var item in coll.Keys) { var exists = schema.Any(x => x.COLUMN_NAME.ToLower() == item.ToString().ToLower()); - if (exists) { - var key = item.ToString(); - var val = coll[key]; - dc.Add(key, val); - } + if (!exists) + continue; + var key = item.ToString(); + var val = coll[key]; + dc.Add(key, val); } return result; } @@ -153,12 +142,17 @@ public class DynamicModel : DynamicObject { string def = column.COLUMN_DEFAULT; if (String.IsNullOrEmpty(def)) { result = null; - } else if (def == "getdate()" || def == "(getdate())") { - result = DateTime.Now.ToShortDateString(); - } else if (def == "newid()") { - result = Guid.NewGuid().ToString(); - } else { - result = def.Replace("(", "").Replace(")", ""); + } else switch (def) { + case "(getdate())": + case "getdate()": + result = DateTime.Now.ToShortDateString(); + break; + case "newid()": + result = Guid.NewGuid().ToString(); + break; + default: + result = def.Replace("(", "").Replace(")", ""); + break; } return result; } @@ -183,39 +177,29 @@ public class DynamicModel : DynamicObject { /// IEnumerable _schema; public IEnumerable Schema { - get { - if (_schema == null) - _schema = Query("SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = @0", TableName); - return _schema; - } + get { return _schema ?? (_schema = Query("SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = @0", TableName)); } } - /// /// Enumerates the reader yielding the result - thanks to Jeroen Haegebaert /// public virtual IEnumerable Query(string sql, params object[] args) { - using (var conn = OpenConnection()) { + using (var conn = OpenConnection()) var rdr = CreateCommand(sql, conn, args).ExecuteReader(); - while (rdr.Read()) { - yield return rdr.RecordToExpando(); ; - } - } + while (rdr.Read()) + yield return rdr.RecordToExpando(); } public virtual IEnumerable Query(string sql, DbConnection connection, params object[] args) { - using (var rdr = CreateCommand(sql, connection, args).ExecuteReader()) { - while (rdr.Read()) { - yield return rdr.RecordToExpando(); ; - } - } + using (var rdr = CreateCommand(sql, connection, args).ExecuteReader()) + while (rdr.Read()) + yield return rdr.RecordToExpando(); } /// /// Returns a single result /// public virtual object Scalar(string sql, params object[] args) { object result = null; - using (var conn = OpenConnection()) { - result = CreateCommand(sql, conn, args).ExecuteScalar(); - } + using (var conn = OpenConnection()) + result = CreateCommand(sql, conn, args).ExecuteScalar(); return result; } /// @@ -225,8 +209,7 @@ public class DynamicModel : DynamicObject { var result = _factory.CreateCommand(); result.Connection = conn; result.CommandText = sql; - if (args.Length > 0) - result.AddParams(args); + if (args.Length > 0) result.AddParams(args); return result; } /// @@ -244,22 +227,11 @@ public class DynamicModel : DynamicObject { /// With a PK property (whatever PrimaryKeyField is set to) will be created at UPDATEs /// public virtual List BuildCommands(params object[] things) { - var commands = new List(); - foreach (var item in things) { - if (HasPrimaryKey(item)) { - commands.Add(CreateUpdateCommand(item, GetPrimaryKey(item))); - } else { - commands.Add(CreateInsertCommand(item)); - } - } - return commands; + return things.Select(item => HasPrimaryKey(item) ? CreateUpdateCommand(item, GetPrimaryKey(item)) : CreateInsertCommand(item)).ToList(); } - - public virtual int Execute(DbCommand command) { - return Execute(new DbCommand[] { command }); + return Execute(new[] { command }); } - public virtual int Execute(string sql, params object[] args) { return Execute(CreateCommand(sql, null, args)); } @@ -268,7 +240,7 @@ public class DynamicModel : DynamicObject { /// public virtual int Execute(IEnumerable commands) { var result = 0; - using (var conn = OpenConnection()) { + using (var conn = OpenConnection()) using (var tx = conn.BeginTransaction()) { foreach (var cmd in commands) { cmd.Connection = conn; @@ -277,10 +249,9 @@ public class DynamicModel : DynamicObject { } tx.Commit(); } - } return result; } - public virtual string PrimaryKeyField { get; set; } + public string PrimaryKeyField { get; set; } /// /// Conventionally introspects the object passed in for a field that /// looks like a PK. If you've named your PrimaryKeyField, this becomes easy @@ -297,7 +268,7 @@ public class DynamicModel : DynamicObject { o.ToDictionary().TryGetValue(PrimaryKeyField, out result); return result; } - public virtual string TableName { get; set; } + public string TableName { get; set; } /// /// Returns all records complying with the passed-in WHERE clause and arguments, /// ordered as specified, limited (TOP) by limit. @@ -314,48 +285,32 @@ public class DynamicModel : DynamicObject { sql += orderBy.Trim().StartsWith("order by", StringComparison.OrdinalIgnoreCase) ? orderBy : " ORDER BY " + orderBy; return sql; } - /// /// Returns a dynamic PagedResult. Result properties are Items, TotalPages, and TotalRecords. /// - public virtual dynamic Paged(string where = "", string orderBy = "", string columns = "*", int pageSize = 20, int currentPage = 1, params object[] args) - { + public virtual dynamic Paged(string where = "", string orderBy = "", string columns = "*", int pageSize = 20, int currentPage = 1, params object[] args) { return BuildPagedResult(where: where, orderBy: orderBy, columns: columns, pageSize: pageSize, currentPage: currentPage, args: args); } - - public virtual dynamic Paged(string sql, string primaryKey, string where = "", string orderBy = "", string columns = "*", int pageSize = 20, int currentPage = 1, params object[] args) - { + public virtual dynamic Paged(string sql, string primaryKey, string where = "", string orderBy = "", string columns = "*", int pageSize = 20, int currentPage = 1, params object[] args) { return BuildPagedResult(sql, primaryKey, where, orderBy, columns, pageSize, currentPage, args); } - - private dynamic BuildPagedResult(string sql = "", string primaryKeyField = "", string where = "", string orderBy = "", string columns = "*", int pageSize = 20, int currentPage = 1, params object[] args) - { + private dynamic BuildPagedResult(string sql = "", string primaryKeyField = "", string where = "", string orderBy = "", string columns = "*", int pageSize = 20, int currentPage = 1, params object[] args) { dynamic result = new ExpandoObject(); var countSQL = ""; - if (!string.IsNullOrEmpty(sql)) - countSQL = string.Format("SELECT COUNT({0}) FROM ({1}) AS PagedTable", primaryKeyField, sql); - else - countSQL = string.Format("SELECT COUNT({0}) FROM {1}", PrimaryKeyField, TableName); - - if (String.IsNullOrEmpty(orderBy)) - { + countSQL = !string.IsNullOrEmpty(sql) + ? string.Format("SELECT COUNT({0}) FROM ({1}) AS PagedTable", primaryKeyField, sql) + : string.Format("SELECT COUNT({0}) FROM {1}", PrimaryKeyField, TableName); + if (String.IsNullOrEmpty(orderBy)){ orderBy = string.IsNullOrEmpty(primaryKeyField) ? PrimaryKeyField : primaryKeyField; } - if (!string.IsNullOrEmpty(where)) - { - if (!where.Trim().StartsWith("where", StringComparison.CurrentCultureIgnoreCase)) - { + if (!where.Trim().StartsWith("where", StringComparison.CurrentCultureIgnoreCase)){ where = " WHERE " + where; - } - } - + } var query = ""; - if (!string.IsNullOrEmpty(sql)) - query = string.Format("SELECT {0} FROM (SELECT ROW_NUMBER() OVER (ORDER BY {2}) AS Row, {0} FROM ({3}) AS PagedTable {4}) AS Paged ", columns, pageSize, orderBy, sql, where); - else - query = string.Format("SELECT {0} FROM (SELECT ROW_NUMBER() OVER (ORDER BY {2}) AS Row, {0} FROM {3} {4}) AS Paged ", columns, pageSize, orderBy, TableName, where); - + query = !string.IsNullOrEmpty(sql) + ? string.Format("SELECT {0} FROM (SELECT ROW_NUMBER() OVER (ORDER BY {2}) AS Row, {0} FROM ({3}) AS PagedTable {4}) AS Paged ", columns, pageSize, orderBy, sql, @where) + : string.Format("SELECT {0} FROM (SELECT ROW_NUMBER() OVER (ORDER BY {2}) AS Row, {0} FROM {3} {4}) AS Paged ", columns, pageSize, orderBy, TableName, @where); var pageStart = (currentPage - 1) * pageSize; query += string.Format(" WHERE Row > {0} AND Row <={1}", pageStart, (pageStart + pageSize)); countSQL += where; @@ -393,12 +348,11 @@ private dynamic BuildPagedResult(string sql = "", string primaryKeyField = "", s var results = Query(sql).ToList().Cast>(); return results.ToDictionary(key => key[PrimaryKeyField].ToString(), value => value[DescriptorField]); } - /// /// This will return an Expando as a Dictionary /// public virtual IDictionary ItemAsDictionary(ExpandoObject item) { - return (IDictionary)item; + return item; } //Checks to see if a key is present based on the passed-in value public virtual bool ItemContainsKey(string key, ExpandoObject item) { @@ -411,11 +365,8 @@ private dynamic BuildPagedResult(string sql = "", string primaryKeyField = "", s /// With a PK property (whatever PrimaryKeyField is set to) will be created at UPDATEs /// public virtual int Save(params object[] things) { - foreach (var item in things) { - if (!IsValid(item)) { - throw new InvalidOperationException("Can't save this item: " + String.Join("; ", Errors.ToArray())); - } - } + if (things.Any(item => !IsValid(item))) + throw new InvalidOperationException("Can't save this item: " + String.Join("; ", Errors.ToArray())); var commands = BuildCommands(things); return Execute(commands); } @@ -448,16 +399,15 @@ private dynamic BuildPagedResult(string sql = "", string primaryKeyField = "", s var settings = (IDictionary)expando; var sbKeys = new StringBuilder(); var stub = "UPDATE {0} SET {1} WHERE {2} = @{3}"; - var args = new List(); var result = CreateCommand(stub, null); int counter = 0; foreach (var item in settings) { var val = item.Value; - if (!item.Key.Equals(PrimaryKeyField, StringComparison.OrdinalIgnoreCase) && item.Value != null) { - result.AddParam(val); - sbKeys.AppendFormat("{0} = @{1}, \r\n", item.Key, counter.ToString()); - counter++; - } + if (item.Key.Equals(PrimaryKeyField, StringComparison.OrdinalIgnoreCase) || item.Value == null) + continue; + result.AddParam(val); + sbKeys.AppendFormat("{0} = @{1}, \r\n", item.Key, counter.ToString()); + counter++; } if (counter > 0) { //add the key @@ -475,43 +425,36 @@ private dynamic BuildPagedResult(string sql = "", string primaryKeyField = "", s var sql = string.Format("DELETE FROM {0} ", TableName); if (key != null) { sql += string.Format("WHERE {0}=@0", PrimaryKeyField); - args = new object[] { key }; - } else if (!string.IsNullOrEmpty(where)) { + args = new[] { key }; + } else if (!string.IsNullOrEmpty(where)) sql += where.Trim().StartsWith("where", StringComparison.OrdinalIgnoreCase) ? where : "WHERE " + where; - } return CreateCommand(sql, null, args); } - public bool IsValid(dynamic item) { Errors.Clear(); Validate(item); return Errors.Count == 0; } - //Temporary holder for error messages - public IList Errors = new List(); + public readonly IList Errors = new List(); /// /// Adds a record to the database. You can pass in an Anonymous object, an ExpandoObject, /// A regular old POCO, or a NameValueColletion from a Request.Form or Request.QueryString /// public virtual dynamic Insert(object o) { var ex = o.ToExpando(); - if (!IsValid(ex)) { + if (!IsValid(ex)) throw new InvalidOperationException("Can't insert: " + String.Join("; ", Errors.ToArray())); + if (!BeforeSave(ex)) return null; + using (dynamic conn = OpenConnection()){ + var cmd = CreateInsertCommand(ex); + cmd.Connection = conn; + cmd.ExecuteNonQuery(); + cmd.CommandText = "SELECT @@IDENTITY as newID"; + ex.ID = cmd.ExecuteScalar(); + Inserted(ex); } - if (BeforeSave(ex)) { - using (dynamic conn = OpenConnection()) { - var cmd = CreateInsertCommand(ex); - cmd.Connection = conn; - cmd.ExecuteNonQuery(); - cmd.CommandText = "SELECT @@IDENTITY as newID"; - ex.ID = cmd.ExecuteScalar(); - Inserted(ex); - } - return ex; - } else { - return null; - } + return ex; } /// /// Updates a record in the database. You can pass in an Anonymous object, an ExpandoObject, @@ -533,7 +476,7 @@ private dynamic BuildPagedResult(string sql = "", string primaryKeyField = "", s /// Removes one or more records from the DB according to the passed-in WHERE /// public int Delete(object key = null, string where = "", params object[] args) { - var deleted = this.Single(key); + var deleted = Single(key); var result = 0; if (BeforeDelete(deleted)) { result = Execute(CreateDeleteCommand(where: where, key: key, args: args)); @@ -541,14 +484,11 @@ private dynamic BuildPagedResult(string sql = "", string primaryKeyField = "", s } return result; } - public void DefaultTo(string key, object value, dynamic item) { - if (!ItemContainsKey(key, item)) { - var dc = (IDictionary)item; - dc[key] = value; - } + if (ItemContainsKey(key, item)) return; + var dc = (IDictionary)item; + dc[key] = value; } - //Hooks public virtual void Validate(dynamic item) { } public virtual void Inserted(dynamic item) { } @@ -556,31 +496,24 @@ private dynamic BuildPagedResult(string sql = "", string primaryKeyField = "", s public virtual void Deleted(dynamic item) { } public virtual bool BeforeDelete(dynamic item) { return true; } public virtual bool BeforeSave(dynamic item) { return true; } - //validation methods public virtual void ValidatesPresenceOf(object value, string message = "Required") { if (value == null) Errors.Add(message); - if (String.IsNullOrEmpty(value.ToString())) + if (value != null && String.IsNullOrEmpty(value.ToString())) Errors.Add(message); } //fun methods public virtual void ValidatesNumericalityOf(object value, string message = "Should be a number") { var type = value.GetType().Name; - var numerics = new string[] { "Int32", "Int16", "Int64", "Decimal", "Double", "Single", "Float" }; - if (!numerics.Contains(type)) { - Errors.Add(message); - } + var numerics = new[] { "Int32", "Int16", "Int64", "Decimal", "Double", "Single", "Float" }; + if (!numerics.Contains(type))Errors.Add(message); } public virtual void ValidateIsCurrency(object value, string message = "Should be money") { - if (value == null) - Errors.Add(message); + if (value == null) Errors.Add(message); decimal val = decimal.MinValue; - decimal.TryParse(value.ToString(), out val); - if (val == decimal.MinValue) - Errors.Add(message); - - + if (value != null) decimal.TryParse(value.ToString(), out val); + if (val == decimal.MinValue) Errors.Add(message); } public int Count() { return Count(TableName); @@ -588,7 +521,6 @@ private dynamic BuildPagedResult(string sql = "", string primaryKeyField = "", s public int Count(string tableName, string where="") { return (int)Scalar("SELECT COUNT(*) FROM " + tableName+" "+where); } - /// /// A helpful query tool /// @@ -605,13 +537,10 @@ private dynamic BuildPagedResult(string sql = "", string primaryKeyField = "", s var op = binder.Name; var columns = " * "; string orderBy = string.Format(" ORDER BY {0}", PrimaryKeyField); - string sql = ""; string where = ""; var whereArgs = new List(); - //loop the named args - see if we have order, columns and constraints if (info.ArgumentNames.Count > 0) { - for (int i = 0; i < args.Length; i++) { var name = info.ArgumentNames[i].ToLower(); switch (name) { @@ -635,37 +564,31 @@ private dynamic BuildPagedResult(string sql = "", string primaryKeyField = "", s where = " WHERE " + string.Join(" AND ", constraints.ToArray()); } //probably a bit much here but... yeah this whole thing needs to be refactored... - if (op.ToLower() == "count") { - result = Scalar("SELECT COUNT(*) FROM " + TableName + where, whereArgs.ToArray()); - } else if (op.ToLower() == "sum") { - result = Scalar("SELECT SUM(" + columns + ") FROM " + TableName + where, whereArgs.ToArray()); - } else if (op.ToLower() == "max") { - result = Scalar("SELECT MAX(" + columns + ") FROM " + TableName + where, whereArgs.ToArray()); - } else if (op.ToLower() == "min") { - result = Scalar("SELECT MIN(" + columns + ") FROM " + TableName + where, whereArgs.ToArray()); - } else if (op.ToLower() == "avg") { - result = Scalar("SELECT AVG(" + columns + ") FROM " + TableName + where, whereArgs.ToArray()); - } else { - - //build the SQL - sql = "SELECT TOP 1 " + columns + " FROM " + TableName + where; - var justOne = op.StartsWith("First") || op.StartsWith("Last") || op.StartsWith("Get") || op.StartsWith("Single"); - - //Be sure to sort by DESC on the PK (PK Sort is the default) - if (op.StartsWith("Last")) { - orderBy = orderBy + " DESC "; - } else { - //default to multiple - sql = "SELECT " + columns + " FROM " + TableName + where; - } - - if (justOne) { - //return a single record - result = Query(sql + orderBy, whereArgs.ToArray()).FirstOrDefault(); - } else { - //return lots - result = Query(sql + orderBy, whereArgs.ToArray()); - } + switch (op.ToLower()) { + case "count": + result = Scalar("SELECT COUNT(*) FROM " + TableName + @where, whereArgs.ToArray()); + break; + case "sum": + result = Scalar("SELECT SUM(" + columns + ") FROM " + TableName + @where, whereArgs.ToArray()); + break; + case "max": + result = Scalar("SELECT MAX(" + columns + ") FROM " + TableName + @where, whereArgs.ToArray()); + break; + case "min": + result = Scalar("SELECT MIN(" + columns + ") FROM " + TableName + @where, whereArgs.ToArray()); + break; + case "avg": + result = Scalar("SELECT AVG(" + columns + ") FROM " + TableName + @where, whereArgs.ToArray()); + break; + default: + //build the SQL + string sql = "SELECT TOP 1 " + columns + " FROM " + TableName + @where; + var justOne = op.StartsWith("First") || op.StartsWith("Last") || op.StartsWith("Get") || op.StartsWith("Single"); + //Be sure to sort by DESC on the PK (PK Sort is the default) + if (op.StartsWith("Last")) orderBy = orderBy + " DESC "; + else sql = "SELECT " + columns + " FROM " + TableName + @where; //default to multiple + result = justOne ? Query(sql + orderBy, whereArgs.ToArray()).FirstOrDefault() : Query(sql + orderBy, whereArgs.ToArray()); + break; } return true; } From cb4d7da580801994f4b8ff1b985c702bc4138060 Mon Sep 17 00:00:00 2001 From: Rodney Foley Date: Tue, 31 Jul 2012 17:58:22 -0700 Subject: [PATCH 2/5] Update Massive.cs Fixed missing curly brace bug I introduced, added one line :( --- Massive.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Massive.cs b/Massive.cs index 9641f2a..8584102 100644 --- a/Massive.cs +++ b/Massive.cs @@ -183,10 +183,11 @@ public class DynamicModel : DynamicObject { /// Enumerates the reader yielding the result - thanks to Jeroen Haegebaert /// public virtual IEnumerable Query(string sql, params object[] args) { - using (var conn = OpenConnection()) + using (var conn = OpenConnection()) { var rdr = CreateCommand(sql, conn, args).ExecuteReader(); while (rdr.Read()) yield return rdr.RecordToExpando(); + } } public virtual IEnumerable Query(string sql, DbConnection connection, params object[] args) { using (var rdr = CreateCommand(sql, connection, args).ExecuteReader()) From 2ef40c3ead6a78738d95a49106bea434f3ebca2f Mon Sep 17 00:00:00 2001 From: Rodney Foley Date: Mon, 6 Aug 2012 12:05:13 -0700 Subject: [PATCH 3/5] Update Massive.cs Provided connection string name if not a valid name will assume it is an actual connection string. cleaned up getting factory --- Massive.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Massive.cs b/Massive.cs index 8584102..ff76f63 100644 --- a/Massive.cs +++ b/Massive.cs @@ -110,11 +110,10 @@ public class DynamicModel : DynamicObject { TableName = tableName == "" ? this.GetType().Name : tableName; PrimaryKeyField = string.IsNullOrEmpty(primaryKeyField) ? "ID" : primaryKeyField; DescriptorField = descriptorField; - var _providerName = "System.Data.SqlClient"; - if(ConfigurationManager.ConnectionStrings[connectionStringName].ProviderName != null) - _providerName = ConfigurationManager.ConnectionStrings[connectionStringName].ProviderName; - _factory = DbProviderFactories.GetFactory(_providerName); - ConnectionString = ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString; + var _providerName = "System.Data.SqlClient"; + var settings = ConfigurationManager.ConnectionStrings[connectionStringName]; + ConnectionString = settings == null ? connectionStringName : settings.ConnectionString; + _factory = DbProviderFactories.GetFactory(settings == null ? _providerName : settings.ProviderName); } /// /// Creates a new Expando from a Form POST - white listed against the columns in the DB From 25b8249a53c27620b60a9d55490277f8d142a0c1 Mon Sep 17 00:00:00 2001 From: Rodney Foley Date: Mon, 6 Aug 2012 12:07:32 -0700 Subject: [PATCH 4/5] Update Massive.Sqlite.cs Provided connection string name if not a valid name will assume it is an actual connection string. Fixed getting factory actually tries to use connection string provider first before using default. --- Massive.Sqlite.cs | 1204 ++++++++++++++++++++++----------------------- 1 file changed, 602 insertions(+), 602 deletions(-) diff --git a/Massive.Sqlite.cs b/Massive.Sqlite.cs index cc13f4c..0d69da9 100644 --- a/Massive.Sqlite.cs +++ b/Massive.Sqlite.cs @@ -1,176 +1,176 @@ -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Configuration; -using System.Data; -using System.Data.Common; -using System.Dynamic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Data.SQLite; - -namespace Massive.SQLite -{ - public static class ObjectExtensions - { - /// - /// Extension method for adding in a bunch of parameters - /// - public static void AddParams(this DbCommand cmd, params object[] args) - { - foreach (var item in args) - { - AddParam(cmd, item); - } - } - /// - /// Extension for adding single parameter - /// - public static void AddParam(this DbCommand cmd, object item) - { - var p = cmd.CreateParameter(); - p.ParameterName = string.Format("@{0}", cmd.Parameters.Count); - if (item == null) - { - p.Value = DBNull.Value; - } - else - { - if (item.GetType() == typeof(Guid)) - { - p.Value = item.ToString(); - p.DbType = DbType.String; - p.Size = 4000; - } - else if (item.GetType() == typeof(ExpandoObject)) - { - var d = (IDictionary)item; - p.Value = d.Values.FirstOrDefault(); - } - else - { - p.Value = item; - } - if (item.GetType() == typeof(string)) - p.Size = ((string)item).Length > 4000 ? -1 : 4000; - } - cmd.Parameters.Add(p); - } - /// - /// Turns an IDataReader to a Dynamic list of things - /// - public static List ToExpandoList(this IDataReader rdr) - { - var result = new List(); - while (rdr.Read()) - { - result.Add(rdr.RecordToExpando()); - } - return result; - } - public static dynamic RecordToExpando(this IDataReader rdr) - { - dynamic e = new ExpandoObject(); - var d = e as IDictionary; - for (int i = 0; i < rdr.FieldCount; i++) - d.Add(rdr.GetName(i), DBNull.Value.Equals(rdr[i]) ? null : rdr[i]); - return e; - } - /// - /// Turns the object into an ExpandoObject - /// - public static dynamic ToExpando(this object o) - { - var result = new ExpandoObject(); - var d = result as IDictionary; //work with the Expando as a Dictionary - if (o.GetType() == typeof(ExpandoObject)) return o; //shouldn't have to... but just in case - if (o.GetType() == typeof(NameValueCollection) || o.GetType().IsSubclassOf(typeof(NameValueCollection))) - { - var nv = (NameValueCollection)o; - nv.Cast().Select(key => new KeyValuePair(key, nv[key])).ToList().ForEach(i => d.Add(i)); - } - else - { - var props = o.GetType().GetProperties(); - foreach (var item in props) - { - d.Add(item.Name, item.GetValue(o, null)); - } - } - return result; - } - /// - /// Turns the object into a Dictionary - /// - public static IDictionary ToDictionary(this object thingy) - { - return (IDictionary)thingy.ToExpando(); - } - } - /// - /// A class that wraps your database table in Dynamic Funtime - /// - public class DynamicModel : DynamicObject - { - DbProviderFactory _factory; - string ConnectionString; - public static DynamicModel Open(string connectionStringName) - { - dynamic dm = new DynamicModel(connectionStringName); - return dm; - } - public DynamicModel(string connectionStringName, string tableName = "", string primaryKeyField = "") - { - TableName = tableName == "" ? this.GetType().Name : tableName; - PrimaryKeyField = string.IsNullOrEmpty(primaryKeyField) ? "ID" : primaryKeyField; - var _providerName = "System.Data.SQLite"; - _factory = DbProviderFactories.GetFactory(_providerName); - ConnectionString = ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString; - _providerName = ConfigurationManager.ConnectionStrings[connectionStringName].ProviderName; - } - - /// - /// Creates a new Expando from a Form POST - white listed against the columns in the DB - /// - public dynamic CreateFrom(NameValueCollection coll) - { - dynamic result = new ExpandoObject(); - var dc = (IDictionary)result; - var schema = Schema; - //loop the collection, setting only what's in the Schema - foreach (var item in coll.Keys) - { - var exists = schema.Any(x => x.COLUMN_NAME.ToLower() == item.ToString().ToLower()); - if (exists) - { - var key = item.ToString(); - var val = coll[key]; - if (!String.IsNullOrEmpty(val)) - { - //what to do here? If it's empty... set it to NULL? - //if it's a string value - let it go through if it's NULLABLE? - //Empty? WTF? - dc.Add(key, val); - } - } - } - return result; - } - /// - /// Gets a default value for the column - /// - public dynamic DefaultValue(dynamic column) - { - dynamic result = null; - string def = column.COLUMN_DEFAULT; - if (String.IsNullOrEmpty(def)) - { - result = null; - } - else if (def.ToUpper() == "CURRENT_TIME") - { - result = DateTime.UtcNow.ToString("HH:mm:ss"); +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Configuration; +using System.Data; +using System.Data.Common; +using System.Dynamic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Data.SQLite; + +namespace Massive.SQLite +{ + public static class ObjectExtensions + { + /// + /// Extension method for adding in a bunch of parameters + /// + public static void AddParams(this DbCommand cmd, params object[] args) + { + foreach (var item in args) + { + AddParam(cmd, item); + } + } + /// + /// Extension for adding single parameter + /// + public static void AddParam(this DbCommand cmd, object item) + { + var p = cmd.CreateParameter(); + p.ParameterName = string.Format("@{0}", cmd.Parameters.Count); + if (item == null) + { + p.Value = DBNull.Value; + } + else + { + if (item.GetType() == typeof(Guid)) + { + p.Value = item.ToString(); + p.DbType = DbType.String; + p.Size = 4000; + } + else if (item.GetType() == typeof(ExpandoObject)) + { + var d = (IDictionary)item; + p.Value = d.Values.FirstOrDefault(); + } + else + { + p.Value = item; + } + if (item.GetType() == typeof(string)) + p.Size = ((string)item).Length > 4000 ? -1 : 4000; + } + cmd.Parameters.Add(p); + } + /// + /// Turns an IDataReader to a Dynamic list of things + /// + public static List ToExpandoList(this IDataReader rdr) + { + var result = new List(); + while (rdr.Read()) + { + result.Add(rdr.RecordToExpando()); + } + return result; + } + public static dynamic RecordToExpando(this IDataReader rdr) + { + dynamic e = new ExpandoObject(); + var d = e as IDictionary; + for (int i = 0; i < rdr.FieldCount; i++) + d.Add(rdr.GetName(i), DBNull.Value.Equals(rdr[i]) ? null : rdr[i]); + return e; + } + /// + /// Turns the object into an ExpandoObject + /// + public static dynamic ToExpando(this object o) + { + var result = new ExpandoObject(); + var d = result as IDictionary; //work with the Expando as a Dictionary + if (o.GetType() == typeof(ExpandoObject)) return o; //shouldn't have to... but just in case + if (o.GetType() == typeof(NameValueCollection) || o.GetType().IsSubclassOf(typeof(NameValueCollection))) + { + var nv = (NameValueCollection)o; + nv.Cast().Select(key => new KeyValuePair(key, nv[key])).ToList().ForEach(i => d.Add(i)); + } + else + { + var props = o.GetType().GetProperties(); + foreach (var item in props) + { + d.Add(item.Name, item.GetValue(o, null)); + } + } + return result; + } + /// + /// Turns the object into a Dictionary + /// + public static IDictionary ToDictionary(this object thingy) + { + return (IDictionary)thingy.ToExpando(); + } + } + /// + /// A class that wraps your database table in Dynamic Funtime + /// + public class DynamicModel : DynamicObject + { + DbProviderFactory _factory; + string ConnectionString; + public static DynamicModel Open(string connectionStringName) + { + dynamic dm = new DynamicModel(connectionStringName); + return dm; + } + public DynamicModel(string connectionStringName, string tableName = "", string primaryKeyField = "") + { + TableName = tableName == "" ? this.GetType().Name : tableName; + PrimaryKeyField = string.IsNullOrEmpty(primaryKeyField) ? "ID" : primaryKeyField; + var _providerName = "System.Data.SQLite"; + var settings = ConfigurationManager.ConnectionStrings[connectionStringName]; + ConnectionString = settings == null ? connectionStringName : settings.ConnectionString; + _factory = DbProviderFactories.GetFactory(settings == null ? _providerName : settings.ProviderName); + } + + /// + /// Creates a new Expando from a Form POST - white listed against the columns in the DB + /// + public dynamic CreateFrom(NameValueCollection coll) + { + dynamic result = new ExpandoObject(); + var dc = (IDictionary)result; + var schema = Schema; + //loop the collection, setting only what's in the Schema + foreach (var item in coll.Keys) + { + var exists = schema.Any(x => x.COLUMN_NAME.ToLower() == item.ToString().ToLower()); + if (exists) + { + var key = item.ToString(); + var val = coll[key]; + if (!String.IsNullOrEmpty(val)) + { + //what to do here? If it's empty... set it to NULL? + //if it's a string value - let it go through if it's NULLABLE? + //Empty? WTF? + dc.Add(key, val); + } + } + } + return result; + } + /// + /// Gets a default value for the column + /// + public dynamic DefaultValue(dynamic column) + { + dynamic result = null; + string def = column.COLUMN_DEFAULT; + if (String.IsNullOrEmpty(def)) + { + result = null; + } + else if (def.ToUpper() == "CURRENT_TIME") + { + result = DateTime.UtcNow.ToString("HH:mm:ss"); } else if (def.ToUpper() == "CURRENT_DATE") { @@ -179,34 +179,34 @@ public dynamic DefaultValue(dynamic column) else if (def.ToUpper() == "CURRENT_TIMESTAMP") { result = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss"); - } - return result; - } - /// - /// Creates an empty Expando set with defaults from the DB - /// - public dynamic Prototype - { - get - { - dynamic result = new ExpandoObject(); - var schema = Schema; - foreach (dynamic column in schema) - { - var dc = (IDictionary)result; - dc.Add(column.COLUMN_NAME, DefaultValue(column)); - } - result._Table = this; - return result; - } - } - /// - /// List out all the schema bits for use with ... whatever - /// - IEnumerable _schema; - public IEnumerable Schema - { - get + } + return result; + } + /// + /// Creates an empty Expando set with defaults from the DB + /// + public dynamic Prototype + { + get + { + dynamic result = new ExpandoObject(); + var schema = Schema; + foreach (dynamic column in schema) + { + var dc = (IDictionary)result; + dc.Add(column.COLUMN_NAME, DefaultValue(column)); + } + result._Table = this; + return result; + } + } + /// + /// List out all the schema bits for use with ... whatever + /// + IEnumerable _schema; + public IEnumerable Schema + { + get { if (_schema == null) { @@ -223,405 +223,405 @@ public IEnumerable Schema } _schema = rows; } - return _schema; - } - } - - /// - /// Enumerates the reader yielding the result - thanks to Jeroen Haegebaert - /// - public virtual IEnumerable Query(string sql, params object[] args) - { - using (var conn = OpenConnection()) - { - var rdr = CreateCommand(sql, conn, args).ExecuteReader(); - while (rdr.Read()) - { - yield return rdr.RecordToExpando(); ; - } - } - } - - - public virtual IEnumerable Query(string sql, DbConnection connection, params object[] args) - { - using (var rdr = CreateCommand(sql, connection, args).ExecuteReader()) - { - while (rdr.Read()) - { - yield return rdr.RecordToExpando(); ; - } - } - } - /// - /// Returns a single result - /// - public virtual object Scalar(string sql, params object[] args) - { - object result = null; - using (var conn = OpenConnection()) - { - result = CreateCommand(sql, conn, args).ExecuteScalar(); - } - return result; - } - /// - /// Creates a DBCommand that you can use for loving your database. - /// - DbCommand CreateCommand(string sql, DbConnection conn, params object[] args) - { - var result = _factory.CreateCommand(); - result.Connection = conn; - result.CommandText = sql; - if (args.Length > 0) - result.AddParams(args); - return result; - } - /// - /// Returns and OpenConnection - /// - public virtual DbConnection OpenConnection() - { - var result = _factory.CreateConnection(); - result.ConnectionString = ConnectionString; - result.Open(); - return result; - } - /// - /// Builds a set of Insert and Update commands based on the passed-on objects. - /// These objects can be POCOs, Anonymous, NameValueCollections, or Expandos. Objects - /// With a PK property (whatever PrimaryKeyField is set to) will be created at UPDATEs - /// - public virtual List BuildCommands(params object[] things) - { - var commands = new List(); - foreach (var item in things) - { - if (HasPrimaryKey(item)) - { - commands.Add(CreateUpdateCommand(item, GetPrimaryKey(item))); - } - else - { - commands.Add(CreateInsertCommand(item)); - } - } - return commands; - } - /// - /// Executes a set of objects as Insert or Update commands based on their property settings, within a transaction. - /// These objects can be POCOs, Anonymous, NameValueCollections, or Expandos. Objects - /// With a PK property (whatever PrimaryKeyField is set to) will be created at UPDATEs - /// - public virtual int Save(params object[] things) - { - var commands = BuildCommands(things); - return Execute(commands); - } - - public virtual int Execute(DbCommand command) - { - return Execute(new DbCommand[] { command }); - } - - public virtual int Execute(string sql, params object[] args) - { - return Execute(CreateCommand(sql, null, args)); - } - /// - /// Executes a series of DBCommands in a transaction - /// - public virtual int Execute(IEnumerable commands) - { - var result = 0; - using (var conn = OpenConnection()) - { - using (var tx = conn.BeginTransaction()) - { - foreach (var cmd in commands) - { - cmd.Connection = conn; - cmd.Transaction = tx; - result += cmd.ExecuteNonQuery(); - } - tx.Commit(); - } - } - return result; - } - public virtual string PrimaryKeyField { get; set; } - /// - /// Conventionally introspects the object passed in for a field that - /// looks like a PK. If you've named your PrimaryKeyField, this becomes easy - /// - public virtual bool HasPrimaryKey(object o) - { - return o.ToDictionary().ContainsKey(PrimaryKeyField); - } - /// - /// If the object passed in has a property with the same name as your PrimaryKeyField - /// it is returned here. - /// - public virtual object GetPrimaryKey(object o) - { - object result = null; - o.ToDictionary().TryGetValue(PrimaryKeyField, out result); - return result; - } - public virtual string TableName { get; set; } - /// - /// Creates a command for use with transactions - internal stuff mostly, but here for you to play with - /// - public virtual DbCommand CreateInsertCommand(object o) - { - DbCommand result = null; - var expando = o.ToExpando(); - var settings = (IDictionary)expando; - var sbKeys = new StringBuilder(); - var sbVals = new StringBuilder(); - var stub = "INSERT INTO {0} ({1}) \r\n VALUES ({2})"; - result = CreateCommand(stub, null); - int counter = 0; - foreach (var item in settings) - { - sbKeys.AppendFormat("{0},", item.Key); - sbVals.AppendFormat("@{0},", counter.ToString()); - result.AddParam(item.Value); - counter++; - } - if (counter > 0) - { - var keys = sbKeys.ToString().Substring(0, sbKeys.Length - 1); - var vals = sbVals.ToString().Substring(0, sbVals.Length - 1); - var sql = string.Format(stub, TableName, keys, vals); - result.CommandText = sql; - } - else throw new InvalidOperationException("Can't parse this object to the database - there are no properties set"); - return result; - } - /// - /// Creates a command for use with transactions - internal stuff mostly, but here for you to play with - /// - public virtual DbCommand CreateUpdateCommand(object o, object key) - { - var expando = o.ToExpando(); - var settings = (IDictionary)expando; - var sbKeys = new StringBuilder(); - var stub = "UPDATE {0} SET {1} WHERE {2} = @{3}"; - var args = new List(); - var result = CreateCommand(stub, null); - int counter = 0; - foreach (var item in settings) - { - var val = item.Value; - if (!item.Key.Equals(PrimaryKeyField, StringComparison.CurrentCultureIgnoreCase) && item.Value != null) - { - result.AddParam(val); - sbKeys.AppendFormat("{0} = @{1}, \r\n", item.Key, counter.ToString()); - counter++; - } - } - if (counter > 0) - { - //add the key - result.AddParam(key); - //strip the last commas - var keys = sbKeys.ToString().Substring(0, sbKeys.Length - 4); - result.CommandText = string.Format(stub, TableName, keys, PrimaryKeyField, counter); - } - else throw new InvalidOperationException("No parsable object was sent in - could not divine any name/value pairs"); - return result; - } - /// - /// Removes one or more records from the DB according to the passed-in WHERE - /// - public virtual DbCommand CreateDeleteCommand(string where = "", object key = null, params object[] args) - { - var sql = string.Format("DELETE FROM {0} ", TableName); - if (key != null) - { - sql += string.Format("WHERE {0}=@0", PrimaryKeyField); - args = new object[] { key }; - } - else if (!string.IsNullOrEmpty(where)) - { - sql += where.Trim().StartsWith("where", StringComparison.CurrentCultureIgnoreCase) ? where : "WHERE " + where; - } - return CreateCommand(sql, null, args); - } - /// - /// Adds a record to the database. You can pass in an Anonymous object, an ExpandoObject, - /// A regular old POCO, or a NameValueColletion from a Request.Form or Request.QueryString - /// - public virtual object Insert(object o) - { - dynamic result = 0; - using (var conn = OpenConnection()) - { - var cmd = CreateInsertCommand(o); - cmd.Connection = conn; - cmd.ExecuteNonQuery(); - cmd.CommandText = "select last_insert_rowid()"; - result = cmd.ExecuteScalar(); - } - return result; - } - /// - /// Updates a record in the database. You can pass in an Anonymous object, an ExpandoObject, - /// A regular old POCO, or a NameValueCollection from a Request.Form or Request.QueryString - /// - public virtual int Update(object o, object key) - { - return Execute(CreateUpdateCommand(o, key)); - } - /// - /// Removes one or more records from the DB according to the passed-in WHERE - /// - public int Delete(object key = null, string where = "", params object[] args) - { - return Execute(CreateDeleteCommand(where: where, key: key, args: args)); - } - /// - /// Returns all records complying with the passed-in WHERE clause and arguments, - /// ordered as specified, limited (TOP) by limit. - /// - public virtual IEnumerable All(string where = "", string orderBy = "", int limit = 0, string columns = "*", params object[] args) - { - string sql = BuildSelect(where, orderBy, limit); - return Query(string.Format(sql, columns, TableName), args); - } - private static string BuildSelect(string where, string orderBy, int limit) - { - string sql = limit > 0 ? "SELECT TOP " + limit + " {0} FROM {1} " : "SELECT {0} FROM {1} "; - if (!string.IsNullOrEmpty(where)) - sql += where.Trim().StartsWith("where", StringComparison.CurrentCultureIgnoreCase) ? where : "WHERE " + where; - if (!String.IsNullOrEmpty(orderBy)) - sql += orderBy.Trim().StartsWith("order by", StringComparison.CurrentCultureIgnoreCase) ? orderBy : " ORDER BY " + orderBy; - return sql; - } - - /// - /// Returns a dynamic PagedResult. Result properties are Items, TotalPages, and TotalRecords. - /// - public virtual dynamic Paged(string where = "", string orderBy = "", string columns = "*", int pageSize = 20, int currentPage = 1, params object[] args) - { - dynamic result = new ExpandoObject(); - var countSQL = string.Format("SELECT COUNT({0}) FROM {1}", PrimaryKeyField, TableName); - if (String.IsNullOrEmpty(orderBy)) - orderBy = PrimaryKeyField; - - if (!string.IsNullOrEmpty(where)) - { - if (!where.Trim().StartsWith("where", StringComparison.CurrentCultureIgnoreCase)) - { - where = "WHERE " + where; - } - } - var sql = string.Format("select {0} FROM {3} {4} ORDER BY {2} ", columns, pageSize, orderBy, TableName, where); - var pageStart = (currentPage - 1) * pageSize; - sql += string.Format(" LIMIT {0},{1}",pageStart, pageSize); - countSQL += where; - result.TotalRecords = Scalar(countSQL, args); - result.TotalPages = result.TotalRecords / pageSize; - if (result.TotalRecords % pageSize > 0) - result.TotalPages += 1; - result.Items = Query(string.Format(sql, columns, TableName), args); - return result; - } - /// - /// Returns a single row from the database - /// - public virtual dynamic Single(string where, params object[] args) - { - var sql = string.Format("SELECT * FROM {0} WHERE {1}", TableName, where); - return Query(sql, args).FirstOrDefault(); - } - /// - /// Returns a single row from the database - /// - public virtual dynamic Single(object key, string columns = "*") - { - var sql = string.Format("SELECT {0} FROM {1} WHERE {2} = @0", columns, TableName, PrimaryKeyField); - return Query(sql, key).FirstOrDefault(); - } - /// - /// A helpful query tool - /// - public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) - { - //parse the method - var constraints = new List(); - var counter = 0; - var info = binder.CallInfo; - // accepting named args only... SKEET! - if (info.ArgumentNames.Count != args.Length) - { - throw new InvalidOperationException("Please use named arguments for this type of query - the column name, orderby, columns, etc"); - } - - - //first should be "FindBy, Last, Single, First" - var op = binder.Name; - var columns = " * "; - string orderBy = string.Format(" ORDER BY {0}", PrimaryKeyField); - string where = ""; - var whereArgs = new List(); - - //loop the named args - see if we have order, columns and constraints - if (info.ArgumentNames.Count > 0) - { - - for (int i = 0; i < args.Length; i++) - { - var name = info.ArgumentNames[i].ToLower(); - switch (name) - { - case "orderby": - orderBy = " ORDER BY " + args[i]; - break; - case "columns": - columns = args[i].ToString(); - break; - default: - constraints.Add(string.Format(" {0} = @{1}", name, counter)); - whereArgs.Add(args[i]); - counter++; - break; - } - } - } - //Build the WHERE bits - if (constraints.Count > 0) - { - where = " WHERE " + string.Join(" AND ", constraints.ToArray()); - } - //build the SQL - string sql = "SELECT TOP 1 " + columns + " FROM " + TableName + where; - var justOne = op.StartsWith("First") || op.StartsWith("Last") || op.StartsWith("Get"); - - //Be sure to sort by DESC on the PK (PK Sort is the default) - if (op.StartsWith("Last")) - { - orderBy = orderBy + " DESC "; - } - else - { - //default to multiple - sql = "SELECT " + columns + " FROM " + TableName + where; - } - - if (justOne) - { - //return a single record - result = Query(sql + orderBy, whereArgs.ToArray()).FirstOrDefault(); - } - else - { - //return lots - result = Query(sql + orderBy, whereArgs.ToArray()); - } - - return true; - } - } + return _schema; + } + } + + /// + /// Enumerates the reader yielding the result - thanks to Jeroen Haegebaert + /// + public virtual IEnumerable Query(string sql, params object[] args) + { + using (var conn = OpenConnection()) + { + var rdr = CreateCommand(sql, conn, args).ExecuteReader(); + while (rdr.Read()) + { + yield return rdr.RecordToExpando(); ; + } + } + } + + + public virtual IEnumerable Query(string sql, DbConnection connection, params object[] args) + { + using (var rdr = CreateCommand(sql, connection, args).ExecuteReader()) + { + while (rdr.Read()) + { + yield return rdr.RecordToExpando(); ; + } + } + } + /// + /// Returns a single result + /// + public virtual object Scalar(string sql, params object[] args) + { + object result = null; + using (var conn = OpenConnection()) + { + result = CreateCommand(sql, conn, args).ExecuteScalar(); + } + return result; + } + /// + /// Creates a DBCommand that you can use for loving your database. + /// + DbCommand CreateCommand(string sql, DbConnection conn, params object[] args) + { + var result = _factory.CreateCommand(); + result.Connection = conn; + result.CommandText = sql; + if (args.Length > 0) + result.AddParams(args); + return result; + } + /// + /// Returns and OpenConnection + /// + public virtual DbConnection OpenConnection() + { + var result = _factory.CreateConnection(); + result.ConnectionString = ConnectionString; + result.Open(); + return result; + } + /// + /// Builds a set of Insert and Update commands based on the passed-on objects. + /// These objects can be POCOs, Anonymous, NameValueCollections, or Expandos. Objects + /// With a PK property (whatever PrimaryKeyField is set to) will be created at UPDATEs + /// + public virtual List BuildCommands(params object[] things) + { + var commands = new List(); + foreach (var item in things) + { + if (HasPrimaryKey(item)) + { + commands.Add(CreateUpdateCommand(item, GetPrimaryKey(item))); + } + else + { + commands.Add(CreateInsertCommand(item)); + } + } + return commands; + } + /// + /// Executes a set of objects as Insert or Update commands based on their property settings, within a transaction. + /// These objects can be POCOs, Anonymous, NameValueCollections, or Expandos. Objects + /// With a PK property (whatever PrimaryKeyField is set to) will be created at UPDATEs + /// + public virtual int Save(params object[] things) + { + var commands = BuildCommands(things); + return Execute(commands); + } + + public virtual int Execute(DbCommand command) + { + return Execute(new DbCommand[] { command }); + } + + public virtual int Execute(string sql, params object[] args) + { + return Execute(CreateCommand(sql, null, args)); + } + /// + /// Executes a series of DBCommands in a transaction + /// + public virtual int Execute(IEnumerable commands) + { + var result = 0; + using (var conn = OpenConnection()) + { + using (var tx = conn.BeginTransaction()) + { + foreach (var cmd in commands) + { + cmd.Connection = conn; + cmd.Transaction = tx; + result += cmd.ExecuteNonQuery(); + } + tx.Commit(); + } + } + return result; + } + public virtual string PrimaryKeyField { get; set; } + /// + /// Conventionally introspects the object passed in for a field that + /// looks like a PK. If you've named your PrimaryKeyField, this becomes easy + /// + public virtual bool HasPrimaryKey(object o) + { + return o.ToDictionary().ContainsKey(PrimaryKeyField); + } + /// + /// If the object passed in has a property with the same name as your PrimaryKeyField + /// it is returned here. + /// + public virtual object GetPrimaryKey(object o) + { + object result = null; + o.ToDictionary().TryGetValue(PrimaryKeyField, out result); + return result; + } + public virtual string TableName { get; set; } + /// + /// Creates a command for use with transactions - internal stuff mostly, but here for you to play with + /// + public virtual DbCommand CreateInsertCommand(object o) + { + DbCommand result = null; + var expando = o.ToExpando(); + var settings = (IDictionary)expando; + var sbKeys = new StringBuilder(); + var sbVals = new StringBuilder(); + var stub = "INSERT INTO {0} ({1}) \r\n VALUES ({2})"; + result = CreateCommand(stub, null); + int counter = 0; + foreach (var item in settings) + { + sbKeys.AppendFormat("{0},", item.Key); + sbVals.AppendFormat("@{0},", counter.ToString()); + result.AddParam(item.Value); + counter++; + } + if (counter > 0) + { + var keys = sbKeys.ToString().Substring(0, sbKeys.Length - 1); + var vals = sbVals.ToString().Substring(0, sbVals.Length - 1); + var sql = string.Format(stub, TableName, keys, vals); + result.CommandText = sql; + } + else throw new InvalidOperationException("Can't parse this object to the database - there are no properties set"); + return result; + } + /// + /// Creates a command for use with transactions - internal stuff mostly, but here for you to play with + /// + public virtual DbCommand CreateUpdateCommand(object o, object key) + { + var expando = o.ToExpando(); + var settings = (IDictionary)expando; + var sbKeys = new StringBuilder(); + var stub = "UPDATE {0} SET {1} WHERE {2} = @{3}"; + var args = new List(); + var result = CreateCommand(stub, null); + int counter = 0; + foreach (var item in settings) + { + var val = item.Value; + if (!item.Key.Equals(PrimaryKeyField, StringComparison.CurrentCultureIgnoreCase) && item.Value != null) + { + result.AddParam(val); + sbKeys.AppendFormat("{0} = @{1}, \r\n", item.Key, counter.ToString()); + counter++; + } + } + if (counter > 0) + { + //add the key + result.AddParam(key); + //strip the last commas + var keys = sbKeys.ToString().Substring(0, sbKeys.Length - 4); + result.CommandText = string.Format(stub, TableName, keys, PrimaryKeyField, counter); + } + else throw new InvalidOperationException("No parsable object was sent in - could not divine any name/value pairs"); + return result; + } + /// + /// Removes one or more records from the DB according to the passed-in WHERE + /// + public virtual DbCommand CreateDeleteCommand(string where = "", object key = null, params object[] args) + { + var sql = string.Format("DELETE FROM {0} ", TableName); + if (key != null) + { + sql += string.Format("WHERE {0}=@0", PrimaryKeyField); + args = new object[] { key }; + } + else if (!string.IsNullOrEmpty(where)) + { + sql += where.Trim().StartsWith("where", StringComparison.CurrentCultureIgnoreCase) ? where : "WHERE " + where; + } + return CreateCommand(sql, null, args); + } + /// + /// Adds a record to the database. You can pass in an Anonymous object, an ExpandoObject, + /// A regular old POCO, or a NameValueColletion from a Request.Form or Request.QueryString + /// + public virtual object Insert(object o) + { + dynamic result = 0; + using (var conn = OpenConnection()) + { + var cmd = CreateInsertCommand(o); + cmd.Connection = conn; + cmd.ExecuteNonQuery(); + cmd.CommandText = "select last_insert_rowid()"; + result = cmd.ExecuteScalar(); + } + return result; + } + /// + /// Updates a record in the database. You can pass in an Anonymous object, an ExpandoObject, + /// A regular old POCO, or a NameValueCollection from a Request.Form or Request.QueryString + /// + public virtual int Update(object o, object key) + { + return Execute(CreateUpdateCommand(o, key)); + } + /// + /// Removes one or more records from the DB according to the passed-in WHERE + /// + public int Delete(object key = null, string where = "", params object[] args) + { + return Execute(CreateDeleteCommand(where: where, key: key, args: args)); + } + /// + /// Returns all records complying with the passed-in WHERE clause and arguments, + /// ordered as specified, limited (TOP) by limit. + /// + public virtual IEnumerable All(string where = "", string orderBy = "", int limit = 0, string columns = "*", params object[] args) + { + string sql = BuildSelect(where, orderBy, limit); + return Query(string.Format(sql, columns, TableName), args); + } + private static string BuildSelect(string where, string orderBy, int limit) + { + string sql = limit > 0 ? "SELECT TOP " + limit + " {0} FROM {1} " : "SELECT {0} FROM {1} "; + if (!string.IsNullOrEmpty(where)) + sql += where.Trim().StartsWith("where", StringComparison.CurrentCultureIgnoreCase) ? where : "WHERE " + where; + if (!String.IsNullOrEmpty(orderBy)) + sql += orderBy.Trim().StartsWith("order by", StringComparison.CurrentCultureIgnoreCase) ? orderBy : " ORDER BY " + orderBy; + return sql; + } + + /// + /// Returns a dynamic PagedResult. Result properties are Items, TotalPages, and TotalRecords. + /// + public virtual dynamic Paged(string where = "", string orderBy = "", string columns = "*", int pageSize = 20, int currentPage = 1, params object[] args) + { + dynamic result = new ExpandoObject(); + var countSQL = string.Format("SELECT COUNT({0}) FROM {1}", PrimaryKeyField, TableName); + if (String.IsNullOrEmpty(orderBy)) + orderBy = PrimaryKeyField; + + if (!string.IsNullOrEmpty(where)) + { + if (!where.Trim().StartsWith("where", StringComparison.CurrentCultureIgnoreCase)) + { + where = "WHERE " + where; + } + } + var sql = string.Format("select {0} FROM {3} {4} ORDER BY {2} ", columns, pageSize, orderBy, TableName, where); + var pageStart = (currentPage - 1) * pageSize; + sql += string.Format(" LIMIT {0},{1}",pageStart, pageSize); + countSQL += where; + result.TotalRecords = Scalar(countSQL, args); + result.TotalPages = result.TotalRecords / pageSize; + if (result.TotalRecords % pageSize > 0) + result.TotalPages += 1; + result.Items = Query(string.Format(sql, columns, TableName), args); + return result; + } + /// + /// Returns a single row from the database + /// + public virtual dynamic Single(string where, params object[] args) + { + var sql = string.Format("SELECT * FROM {0} WHERE {1}", TableName, where); + return Query(sql, args).FirstOrDefault(); + } + /// + /// Returns a single row from the database + /// + public virtual dynamic Single(object key, string columns = "*") + { + var sql = string.Format("SELECT {0} FROM {1} WHERE {2} = @0", columns, TableName, PrimaryKeyField); + return Query(sql, key).FirstOrDefault(); + } + /// + /// A helpful query tool + /// + public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) + { + //parse the method + var constraints = new List(); + var counter = 0; + var info = binder.CallInfo; + // accepting named args only... SKEET! + if (info.ArgumentNames.Count != args.Length) + { + throw new InvalidOperationException("Please use named arguments for this type of query - the column name, orderby, columns, etc"); + } + + + //first should be "FindBy, Last, Single, First" + var op = binder.Name; + var columns = " * "; + string orderBy = string.Format(" ORDER BY {0}", PrimaryKeyField); + string where = ""; + var whereArgs = new List(); + + //loop the named args - see if we have order, columns and constraints + if (info.ArgumentNames.Count > 0) + { + + for (int i = 0; i < args.Length; i++) + { + var name = info.ArgumentNames[i].ToLower(); + switch (name) + { + case "orderby": + orderBy = " ORDER BY " + args[i]; + break; + case "columns": + columns = args[i].ToString(); + break; + default: + constraints.Add(string.Format(" {0} = @{1}", name, counter)); + whereArgs.Add(args[i]); + counter++; + break; + } + } + } + //Build the WHERE bits + if (constraints.Count > 0) + { + where = " WHERE " + string.Join(" AND ", constraints.ToArray()); + } + //build the SQL + string sql = "SELECT TOP 1 " + columns + " FROM " + TableName + where; + var justOne = op.StartsWith("First") || op.StartsWith("Last") || op.StartsWith("Get"); + + //Be sure to sort by DESC on the PK (PK Sort is the default) + if (op.StartsWith("Last")) + { + orderBy = orderBy + " DESC "; + } + else + { + //default to multiple + sql = "SELECT " + columns + " FROM " + TableName + where; + } + + if (justOne) + { + //return a single record + result = Query(sql + orderBy, whereArgs.ToArray()).FirstOrDefault(); + } + else + { + //return lots + result = Query(sql + orderBy, whereArgs.ToArray()); + } + + return true; + } + } } \ No newline at end of file From 1e265d6995b3072d2a1454ae4027f0cce45701aa Mon Sep 17 00:00:00 2001 From: Rodney Foley Date: Mon, 6 Aug 2012 12:12:05 -0700 Subject: [PATCH 5/5] Update Massive.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added ExecuteWithReturn that can support procs and namedArgs Added additional CreateCommand method to support ExecuteWithReturn --- Massive.cs | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/Massive.cs b/Massive.cs index ff76f63..a994930 100644 --- a/Massive.cs +++ b/Massive.cs @@ -211,6 +211,19 @@ public class DynamicModel : DynamicObject { result.CommandText = sql; if (args.Length > 0) result.AddParams(args); return result; + } + DbCommand CreateCommand(string sql, DbConnection conn, bool isProc, Dictionary namedArgs) + { + SqlCommand cmd = _factory.CreateCommand() as SqlCommand; + cmd.Connection = conn as SqlConnection; + cmd.CommandText = sql; + foreach(var kvp in namedArgs) + cmd.Parameters.AddWithValue(kvp.Key, kvp.Value); + if (isProc){ + cmd.CommandType = CommandType.StoredProcedure; + cmd.Parameters.Add("@returnValue", SqlDbType.Int).Direction = ParameterDirection.ReturnValue; ; + } + return cmd; } /// /// Returns and OpenConnection @@ -235,20 +248,31 @@ public class DynamicModel : DynamicObject { public virtual int Execute(string sql, params object[] args) { return Execute(CreateCommand(sql, null, args)); } + public virtual int ExecuteWithReturn(string sql, bool isProc, Dictionary namedArgs) + { + return Execute(CreateCommand(sql, null, isProc, namedArgs)); + } /// /// Executes a series of DBCommands in a transaction /// public virtual int Execute(IEnumerable commands) { var result = 0; using (var conn = OpenConnection()) - using (var tx = conn.BeginTransaction()) { - foreach (var cmd in commands) { - cmd.Connection = conn; - cmd.Transaction = tx; + using (var tx = conn.BeginTransaction()) { + foreach (var cmd in commands) { + cmd.Connection = conn; + cmd.Transaction = tx; + if (cmd.CommandType == CommandType.StoredProcedure){ + cmd.ExecuteNonQuery(); + if (cmd.Parameters["@returnValue"].Value != null) + result += Int32.Parse(cmd.Parameters["@returnValue"].Value.ToString()); + else + result += -1; + } else result += cmd.ExecuteNonQuery(); - } - tx.Commit(); } + tx.Commit(); + } return result; } public string PrimaryKeyField { get; set; }