Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Added async methods (thanks to Damien Edwards) and Rails ActiveRecord…

… style querying
  • Loading branch information...
commit bdfd2a2afec59bbf658a95cfc45b203f478618c4 1 parent 8964785
SubSonic Project authored June 08, 2011

Showing 1 changed file with 414 additions and 362 deletions. Show diff stats Hide diff stats

  1. 776  Massive.cs
776  Massive.cs
... ...
@@ -1,364 +1,416 @@
1  
-using System;
2  
-using System.Collections.Generic;
3  
-using System.Collections.Specialized;
4  
-using System.Configuration;
5  
-using System.Data;
6  
-using System.Data.Common;
7  
-using System.Dynamic;
8  
-using System.Linq;
9  
-using System.Text;
10  
-
11  
-namespace Massive {
12  
-    public static class ObjectExtensions {
13  
-        /// <summary>
14  
-        /// Extension method for adding in a bunch of parameters
15  
-        /// </summary>
16  
-        public static void AddParams(this DbCommand cmd, params object[] args) {
17  
-            foreach (var item in args) {
18  
-                AddParam(cmd, item);
19  
-            }
20  
-        }
21  
-        /// <summary>
22  
-        /// Extension for adding single parameter
23  
-        /// </summary>
24  
-        public static void AddParam(this DbCommand cmd, object item) {
25  
-            var p = cmd.CreateParameter();
26  
-            p.ParameterName = string.Format("@{0}", cmd.Parameters.Count);
27  
-            if (item == null) {
28  
-                p.Value = DBNull.Value;
29  
-            } else {
30  
-                if (item.GetType() == typeof(Guid)) {
31  
-                    p.Value = item.ToString();
32  
-                    p.DbType = DbType.String;
33  
-                    p.Size = 4000;
34  
-                } else if (item.GetType() == typeof(ExpandoObject)) {
35  
-                    var d = (IDictionary<string, object>)item;
36  
-                    p.Value = d.Values.FirstOrDefault();
37  
-                } else {
38  
-                    p.Value = item;
39  
-                }
40  
-                if (item.GetType() == typeof(string))
41  
-                    p.Size = ((string)item).Length > 4000 ? -1 : 4000;
42  
-            }
43  
-            cmd.Parameters.Add(p);
44  
-        }
45  
-        /// <summary>
46  
-        /// Turns an IDataReader to a Dynamic list of things
47  
-        /// </summary>
48  
-        public static List<dynamic> ToExpandoList(this IDataReader rdr) {
49  
-            var result = new List<dynamic>();
50  
-            while (rdr.Read()) {
51  
-                result.Add(rdr.RecordToExpando());
52  
-            }
53  
-            return result;
54  
-        }
55  
-        public static dynamic RecordToExpando(this IDataReader rdr) {
56  
-            dynamic e = new ExpandoObject();
57  
-            var d = e as IDictionary<string, object>;
  1
+using System;
  2
+using System.Collections.Generic;
  3
+using System.Collections.Specialized;
  4
+using System.Configuration;
  5
+using System.Data;
  6
+using System.Data.Common;
  7
+using System.Dynamic;
  8
+using System.Linq;
  9
+using System.Text;
  10
+using System.Threading.Tasks;
  11
+using System.Data.SqlClient;
  12
+
  13
+namespace Massive {
  14
+    public static class ObjectExtensions {
  15
+        /// <summary>
  16
+        /// Extension method for adding in a bunch of parameters
  17
+        /// </summary>
  18
+        public static void AddParams(this DbCommand cmd, params object[] args) {
  19
+            foreach (var item in args) {
  20
+                AddParam(cmd, item);
  21
+            }
  22
+        }
  23
+        /// <summary>
  24
+        /// Extension for adding single parameter
  25
+        /// </summary>
  26
+        public static void AddParam(this DbCommand cmd, object item) {
  27
+            var p = cmd.CreateParameter();
  28
+            p.ParameterName = string.Format("@{0}", cmd.Parameters.Count);
  29
+            if (item == null) {
  30
+                p.Value = DBNull.Value;
  31
+            } else {
  32
+                if (item.GetType() == typeof(Guid)) {
  33
+                    p.Value = item.ToString();
  34
+                    p.DbType = DbType.String;
  35
+                    p.Size = 4000;
  36
+                } else if (item.GetType() == typeof(ExpandoObject)) {
  37
+                    var d = (IDictionary<string, object>)item;
  38
+                    p.Value = d.Values.FirstOrDefault();
  39
+                } else {
  40
+                    p.Value = item;
  41
+                }
  42
+                if (item.GetType() == typeof(string))
  43
+                    p.Size = ((string)item).Length > 4000 ? -1 : 4000;
  44
+            }
  45
+            cmd.Parameters.Add(p);
  46
+        }
  47
+        /// <summary>
  48
+        /// Turns an IDataReader to a Dynamic list of things
  49
+        /// </summary>
  50
+        public static List<dynamic> ToExpandoList(this IDataReader rdr) {
  51
+            var result = new List<dynamic>();
  52
+            while (rdr.Read()) {
  53
+                result.Add(rdr.RecordToExpando());
  54
+            }
  55
+            return result;
  56
+        }
  57
+        public static dynamic RecordToExpando(this IDataReader rdr) {
  58
+            dynamic e = new ExpandoObject();
  59
+            var d = e as IDictionary<string, object>;
58 60
             for (int i = 0; i < rdr.FieldCount; i++)
59  
-                d.Add(rdr.GetName(i), DBNull.Value.Equals(rdr[i]) ? null : rdr[i]);
60  
-            return e;
61  
-        }
62  
-        /// <summary>
63  
-        /// Turns the object into an ExpandoObject
64  
-        /// </summary>
65  
-        public static dynamic ToExpando(this object o) {
66  
-            var result = new ExpandoObject();
67  
-            var d = result as IDictionary<string, object>; //work with the Expando as a Dictionary
68  
-            if (o.GetType() == typeof(ExpandoObject)) return o; //shouldn't have to... but just in case
69  
-            if (o.GetType() == typeof(NameValueCollection) || o.GetType().IsSubclassOf(typeof(NameValueCollection))) {
70  
-                var nv = (NameValueCollection)o;
71  
-                nv.Cast<string>().Select(key => new KeyValuePair<string, object>(key, nv[key])).ToList().ForEach(i => d.Add(i));
72  
-            } else {
73  
-                var props = o.GetType().GetProperties();
74  
-                foreach (var item in props) {
75  
-                    d.Add(item.Name, item.GetValue(o, null));
76  
-                }
77  
-            }
78  
-            return result;
79  
-        }
80  
-        /// <summary>
81  
-        /// Turns the object into a Dictionary
82  
-        /// </summary>
83  
-        public static IDictionary<string, object> ToDictionary(this object thingy) {
84  
-            return (IDictionary<string, object>)thingy.ToExpando();
85  
-        }
86  
-    }
87  
-    /// <summary>
88  
-    /// A class that wraps your database table in Dynamic Funtime
89  
-    /// </summary>
90  
-    public class DynamicModel {
91  
-        DbProviderFactory _factory;
92  
-        string _connectionString;
93  
-
94  
-        public DynamicModel(string connectionStringName = "", string tableName = "", string primaryKeyField = "") {
95  
-            TableName = tableName == "" ? this.GetType().Name : tableName;
96  
-            PrimaryKeyField = string.IsNullOrEmpty(primaryKeyField) ? "ID" : primaryKeyField;
97  
-            if (connectionStringName == "")
98  
-                connectionStringName = ConfigurationManager.ConnectionStrings[0].Name;
99  
-            var _providerName = "System.Data.SqlClient";
100  
-            if (ConfigurationManager.ConnectionStrings[connectionStringName] != null) {
101  
-                if (!string.IsNullOrEmpty(ConfigurationManager.ConnectionStrings[connectionStringName].ProviderName))
102  
-                    _providerName = ConfigurationManager.ConnectionStrings[connectionStringName].ProviderName;
103  
-            } else {
104  
-                throw new InvalidOperationException("Can't find a connection string with the name '" + connectionStringName + "'");
105  
-            }
106  
-            _factory = DbProviderFactories.GetFactory(_providerName);
107  
-            _connectionString = ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString;
108  
-        }
109  
-        /// <summary>
110  
-        /// Enumerates the reader yielding the result - thanks to Jeroen Haegebaert
111  
-        /// </summary>
112  
-        public virtual IEnumerable<dynamic> Query(string sql, params object[] args) {
113  
-            using (var conn = OpenConnection()) {
114  
-                var rdr = CreateCommand(sql, conn, args).ExecuteReader();
115  
-                while (rdr.Read()) {
116  
-                    yield return rdr.RecordToExpando(); ;
117  
-                }
118  
-            }
119  
-        }
120  
-        public virtual IEnumerable<dynamic> Query(string sql, DbConnection connection, params object[] args) {
121  
-            using (var rdr = CreateCommand(sql, connection, args).ExecuteReader()) {
122  
-                while (rdr.Read()) {
123  
-                    yield return rdr.RecordToExpando(); ;
124  
-                }
125  
-            }
126  
-
127  
-        }
128  
-        /// <summary>
129  
-        /// Returns a single result
130  
-        /// </summary>
131  
-        public virtual object Scalar(string sql, params object[] args) {
132  
-            object result = null;
133  
-            using (var conn = OpenConnection()) {
134  
-                result = CreateCommand(sql, conn, args).ExecuteScalar();
135  
-            }
136  
-            return result;
137  
-        }
138  
-        /// <summary>
139  
-        /// Creates a DBCommand that you can use for loving your database.
140  
-        /// </summary>
141  
-        DbCommand CreateCommand(string sql, DbConnection conn, params object[] args) {
142  
-            var result = _factory.CreateCommand();
143  
-            result.Connection = conn;
144  
-            result.CommandText = sql;
145  
-            if (args.Length > 0)
146  
-                result.AddParams(args);
147  
-            return result;
148  
-        }
149  
-        /// <summary>
150  
-        /// Returns and OpenConnection
151  
-        /// </summary>
152  
-        public virtual DbConnection OpenConnection() {
153  
-            var result = _factory.CreateConnection();
154  
-            result.ConnectionString = _connectionString;
155  
-            result.Open();
156  
-            return result;
157  
-        }
158  
-        /// <summary>
159  
-        /// Builds a set of Insert and Update commands based on the passed-on objects.
160  
-        /// These objects can be POCOs, Anonymous, NameValueCollections, or Expandos. Objects
161  
-        /// With a PK property (whatever PrimaryKeyField is set to) will be created at UPDATEs
162  
-        /// </summary>
163  
-        public virtual List<DbCommand> BuildCommands(params object[] things) {
164  
-            var commands = new List<DbCommand>();
165  
-            foreach (var item in things) {
166  
-                if (HasPrimaryKey(item)) {
167  
-                    commands.Add(CreateUpdateCommand(item, GetPrimaryKey(item)));
168  
-                } else {
169  
-                    commands.Add(CreateInsertCommand(item));
170  
-                }
171  
-            }
172  
-
173  
-            return commands;
174  
-        }
175  
-        /// <summary>
176  
-        /// Executes a set of objects as Insert or Update commands based on their property settings, within a transaction.
177  
-        /// These objects can be POCOs, Anonymous, NameValueCollections, or Expandos. Objects
178  
-        /// With a PK property (whatever PrimaryKeyField is set to) will be created at UPDATEs
179  
-        /// </summary>
180  
-        public virtual int Save(params object[] things) {
181  
-            var commands = BuildCommands(things);
182  
-            return Execute(commands);
183  
-        }
184  
-        public virtual int Execute(DbCommand command) {
185  
-            return Execute(new DbCommand[] { command });
186  
-        }
187  
-        /// <summary>
188  
-        /// Executes a series of DBCommands in a transaction
189  
-        /// </summary>
190  
-        public virtual int Execute(IEnumerable<DbCommand> commands) {
191  
-            var result = 0;
192  
-            using (var conn = OpenConnection()) {
193  
-                using (var tx = conn.BeginTransaction()) {
194  
-                    foreach (var cmd in commands) {
195  
-                        cmd.Connection = conn;
196  
-                        cmd.Transaction = tx;
197  
-                        result += cmd.ExecuteNonQuery();
198  
-                    }
199  
-                    tx.Commit();
200  
-                }
201  
-            }
202  
-            return result;
203  
-        }
204  
-        public virtual string PrimaryKeyField { get; set; }
205  
-        /// <summary>
206  
-        /// Conventionally introspects the object passed in for a field that 
207  
-        /// looks like a PK. If you've named your PrimaryKeyField, this becomes easy
208  
-        /// </summary>
209  
-        public virtual bool HasPrimaryKey(object o) {
210  
-            return o.ToDictionary().ContainsKey(PrimaryKeyField);
211  
-        }
212  
-        /// <summary>
213  
-        /// If the object passed in has a property with the same name as your PrimaryKeyField
214  
-        /// it is returned here.
215  
-        /// </summary>
216  
-        public virtual object GetPrimaryKey(object o) {
217  
-            object result = null;
218  
-            o.ToDictionary().TryGetValue(PrimaryKeyField, out result);
219  
-            return result;
220  
-        }
221  
-        public virtual string TableName { get; set; }
222  
-        /// <summary>
223  
-        /// Creates a command for use with transactions - internal stuff mostly, but here for you to play with
224  
-        /// </summary>
225  
-        public virtual DbCommand CreateInsertCommand(object o) {
226  
-            DbCommand result = null;
227  
-            var expando = o.ToExpando();
228  
-            var settings = (IDictionary<string, object>)expando;
229  
-            var sbKeys = new StringBuilder();
230  
-            var sbVals = new StringBuilder();
231  
-            var stub = "INSERT INTO {0} ({1}) \r\n VALUES ({2})";
232  
-            result = CreateCommand(stub, null);
233  
-            int counter = 0;
234  
-            foreach (var item in settings) {
235  
-                sbKeys.AppendFormat("{0},", item.Key);
236  
-                sbVals.AppendFormat("@{0},", counter.ToString());
237  
-                result.AddParam(item.Value);
238  
-                counter++;
239  
-            }
240  
-            if (counter > 0) {
241  
-                var keys = sbKeys.ToString().Substring(0, sbKeys.Length - 1);
242  
-                var vals = sbVals.ToString().Substring(0, sbVals.Length - 1);
243  
-                var sql = string.Format(stub, TableName, keys, vals);
244  
-                result.CommandText = sql;
245  
-            } else throw new InvalidOperationException("Can't parse this object to the database - there are no properties set");
246  
-            return result;
247  
-        }
248  
-        /// <summary>
249  
-        /// Creates a command for use with transactions - internal stuff mostly, but here for you to play with
250  
-        /// </summary>
251  
-        public virtual DbCommand CreateUpdateCommand(object o, object key) {
252  
-            var expando = o.ToExpando();
253  
-            var settings = (IDictionary<string, object>)expando;
254  
-            var sbKeys = new StringBuilder();
255  
-            var stub = "UPDATE {0} SET {1} WHERE {2} = @{3}";
256  
-            var args = new List<object>();
257  
-            var result = CreateCommand(stub, null);
258  
-            int counter = 0;
259  
-            foreach (var item in settings) {
260  
-                var val = item.Value;
261  
-                if (!item.Key.Equals(PrimaryKeyField, StringComparison.CurrentCultureIgnoreCase) && item.Value != null) {
262  
-                    result.AddParam(val);
263  
-                    sbKeys.AppendFormat("{0} = @{1}, \r\n", item.Key, counter.ToString());
264  
-                    counter++;
265  
-                }
266  
-            }
267  
-            if (counter > 0) {
268  
-                //add the key
269  
-                result.AddParam(key);
270  
-                //strip the last commas
271  
-                var keys = sbKeys.ToString().Substring(0, sbKeys.Length - 4);
272  
-                result.CommandText = string.Format(stub, TableName, keys, PrimaryKeyField, counter);
273  
-            } else throw new InvalidOperationException("No parsable object was sent in - could not divine any name/value pairs");
274  
-            return result;
275  
-        }
276  
-        /// <summary>
277  
-        /// Removes one or more records from the DB according to the passed-in WHERE
278  
-        /// </summary>
279  
-        public virtual DbCommand CreateDeleteCommand(string where = "", object key = null, params object[] args) {
280  
-            var sql = string.Format("DELETE FROM {0} ", TableName);
281  
-            if (key != null) {
282  
-                sql += string.Format("WHERE {0}=@0", PrimaryKeyField);
283  
-                args = new object[] { key };
284  
-            } else if (!string.IsNullOrEmpty(where)) {
285  
-                sql += where.Trim().StartsWith("where", StringComparison.CurrentCultureIgnoreCase) ? where : "WHERE " + where;
286  
-            }
287  
-            return CreateCommand(sql, null, args);
288  
-        }
289  
-        /// <summary>
290  
-        /// Adds a record to the database. You can pass in an Anonymous object, an ExpandoObject,
291  
-        /// A regular old POCO, or a NameValueColletion from a Request.Form or Request.QueryString
292  
-        /// </summary>
293  
-        public virtual object Insert(object o) {
294  
-            dynamic result = 0;
295  
-            using (var conn = OpenConnection()) {
296  
-                var cmd = CreateInsertCommand(o);
297  
-                cmd.Connection = conn;
298  
-                cmd.ExecuteNonQuery();
299  
-                cmd.CommandText = "SELECT @@IDENTITY as newID";
300  
-                result = cmd.ExecuteScalar();
301  
-            }
302  
-            return result;
303  
-        }
304  
-        /// <summary>
305  
-        /// Updates a record in the database. You can pass in an Anonymous object, an ExpandoObject,
306  
-        /// A regular old POCO, or a NameValueCollection from a Request.Form or Request.QueryString
307  
-        /// </summary>
308  
-        public virtual int Update(object o, object key) {
309  
-            return Execute(CreateUpdateCommand(o, key));
310  
-        }
311  
-        /// <summary>
312  
-        /// Removes one or more records from the DB according to the passed-in WHERE
313  
-        /// </summary>
314  
-        public int Delete(object key = null, string where = "", params object[] args) {
315  
-            return Execute(CreateDeleteCommand(where: where, key: key, args: args));
316  
-        }
317  
-        /// <summary>
318  
-        /// Returns all records complying with the passed-in WHERE clause and arguments, 
319  
-        /// ordered as specified, limited (TOP) by limit.
320  
-        /// </summary>
321  
-        public virtual IEnumerable<dynamic> All(string where = "", string orderBy = "", int limit = 0, string columns = "*", params object[] args) {
322  
-            string sql = limit > 0 ? "SELECT TOP " + limit + " {0} FROM {1} " : "SELECT {0} FROM {1} ";
323  
-            if (!string.IsNullOrEmpty(where))
324  
-                sql += where.Trim().StartsWith("where", StringComparison.CurrentCultureIgnoreCase) ? where : "WHERE " + where;
325  
-            if (!String.IsNullOrEmpty(orderBy))
326  
-                sql += orderBy.Trim().StartsWith("order by", StringComparison.CurrentCultureIgnoreCase) ? orderBy : " ORDER BY " + orderBy;
327  
-            return Query(string.Format(sql, columns, TableName), args);
328  
-        }
329  
-
330  
-        /// <summary>
331  
-        /// Returns a dynamic PagedResult. Result properties are Items, TotalPages, and TotalRecords.
332  
-        /// </summary>
333  
-        public virtual dynamic Paged(string where = "", string orderBy = "", string columns = "*", int pageSize = 20, int currentPage = 1, params object[] args) {
334  
-            dynamic result = new ExpandoObject();
335  
-            var countSQL = string.Format("SELECT COUNT({0}) FROM {1}", PrimaryKeyField, TableName);
336  
-            if (String.IsNullOrEmpty(orderBy))
337  
-                orderBy = PrimaryKeyField;
338  
-
339  
-            if (!string.IsNullOrEmpty(where)) {
340  
-                if (!where.Trim().StartsWith("where", StringComparison.CurrentCultureIgnoreCase)) {
341  
-                    where = "WHERE " + where;
342  
-                }
343  
-            }
344  
-            var sql = 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);
345  
-            var pageStart = (currentPage - 1) * pageSize;
346  
-            sql += string.Format(" WHERE Row > {0} AND Row <={1}", pageStart, (pageStart + pageSize));
347  
-            countSQL += where;
348  
-            result.TotalRecords = Scalar(countSQL, args);
349  
-            result.TotalPages = result.TotalRecords / pageSize;
350  
-            if (result.TotalRecords % pageSize > 0)
351  
-                result.TotalPages += 1;
352  
-            result.Items = Query(string.Format(sql, columns, TableName), args);
353  
-            return result;
354  
-        }
355  
-        /// <summary>
356  
-        /// Returns a single row from the database
357  
-        /// </summary>
358  
-        public virtual dynamic Single(object key, string columns = "*") {
359  
-            var sql = string.Format("SELECT {0} FROM {1} WHERE {2} = @0", columns, TableName, PrimaryKeyField);
360  
-            var items = Query(sql, key).ToList();
361  
-            return items.FirstOrDefault();
362  
-        }
363  
-    }
  61
+                d.Add(rdr.GetName(i), DBNull.Value.Equals(rdr[i]) ? null : rdr[i]);
  62
+            return e;
  63
+        }
  64
+        /// <summary>
  65
+        /// Turns the object into an ExpandoObject
  66
+        /// </summary>
  67
+        public static dynamic ToExpando(this object o) {
  68
+            var result = new ExpandoObject();
  69
+            var d = result as IDictionary<string, object>; //work with the Expando as a Dictionary
  70
+            if (o.GetType() == typeof(ExpandoObject)) return o; //shouldn't have to... but just in case
  71
+            if (o.GetType() == typeof(NameValueCollection) || o.GetType().IsSubclassOf(typeof(NameValueCollection))) {
  72
+                var nv = (NameValueCollection)o;
  73
+                nv.Cast<string>().Select(key => new KeyValuePair<string, object>(key, nv[key])).ToList().ForEach(i => d.Add(i));
  74
+            } else {
  75
+                var props = o.GetType().GetProperties();
  76
+                foreach (var item in props) {
  77
+                    d.Add(item.Name, item.GetValue(o, null));
  78
+                }
  79
+            }
  80
+            return result;
  81
+        }
  82
+        /// <summary>
  83
+        /// Turns the object into a Dictionary
  84
+        /// </summary>
  85
+        public static IDictionary<string, object> ToDictionary(this object thingy) {
  86
+            return (IDictionary<string, object>)thingy.ToExpando();
  87
+        }
  88
+    }
  89
+    /// <summary>
  90
+    /// A class that wraps your database table in Dynamic Funtime
  91
+    /// </summary>
  92
+    public class DynamicModel:DynamicObject {
  93
+        DbProviderFactory _factory;
  94
+        string _connectionString;
  95
+
  96
+        public DynamicModel(string connectionStringName = "", string tableName = "", string primaryKeyField = "") {
  97
+            TableName = tableName == "" ? this.GetType().Name : tableName;
  98
+            PrimaryKeyField = string.IsNullOrEmpty(primaryKeyField) ? "ID" : primaryKeyField;
  99
+            if (connectionStringName == "")
  100
+                connectionStringName = ConfigurationManager.ConnectionStrings[0].Name;
  101
+            var _providerName = "System.Data.SqlClient";
  102
+            if (ConfigurationManager.ConnectionStrings[connectionStringName] != null) {
  103
+                if (!string.IsNullOrEmpty(ConfigurationManager.ConnectionStrings[connectionStringName].ProviderName))
  104
+                    _providerName = ConfigurationManager.ConnectionStrings[connectionStringName].ProviderName;
  105
+            } else {
  106
+                throw new InvalidOperationException("Can't find a connection string with the name '" + connectionStringName + "'");
  107
+            }
  108
+            _factory = DbProviderFactories.GetFactory(_providerName);
  109
+            _connectionString = ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString;
  110
+        }
  111
+        /// <summary>
  112
+        /// Enumerates the reader yielding the result - thanks to Jeroen Haegebaert
  113
+        /// </summary>
  114
+        public virtual IEnumerable<dynamic> Query(string sql, params object[] args) {
  115
+            using (var conn = OpenConnection()) {
  116
+                var rdr = CreateCommand(sql, conn, args).ExecuteReader();
  117
+                while (rdr.Read()) {
  118
+                    yield return rdr.RecordToExpando(); ;
  119
+                }
  120
+            }
  121
+        }
  122
+        /// <summary>
  123
+        /// Enumerates the reader yielding the result - thanks to Jeroen Haegebaert
  124
+        /// </summary>
  125
+        public void QueryAsync(string sql, Action<List<dynamic>> callback, params object[] args) {
  126
+            using (var conn = OpenConnection()) {
  127
+                var cmd = new SqlCommand(sql,new SqlConnection(_connectionString));
  128
+                cmd.AddParams(args);
  129
+                cmd.Connection.Open();                
  130
+                var task = Task.Factory.FromAsync<IDataReader>(cmd.BeginExecuteReader, cmd.EndExecuteReader, null);
  131
+                task.ContinueWith(x => callback.Invoke(x.Result.ToExpandoList()));
  132
+            }
  133
+        }
  134
+
  135
+
  136
+        public virtual IEnumerable<dynamic> Query(string sql, DbConnection connection, params object[] args) {
  137
+            using (var rdr = CreateCommand(sql, connection, args).ExecuteReader()) {
  138
+                while (rdr.Read()) {
  139
+                    yield return rdr.RecordToExpando(); ;
  140
+                }
  141
+            }
  142
+
  143
+        }
  144
+        /// <summary>
  145
+        /// Returns a single result
  146
+        /// </summary>
  147
+        public virtual object Scalar(string sql, params object[] args) {
  148
+            object result = null;
  149
+            using (var conn = OpenConnection()) {
  150
+                result = CreateCommand(sql, conn, args).ExecuteScalar();
  151
+            }
  152
+            return result;
  153
+        }
  154
+        /// <summary>
  155
+        /// Creates a DBCommand that you can use for loving your database.
  156
+        /// </summary>
  157
+        DbCommand CreateCommand(string sql, DbConnection conn, params object[] args) {
  158
+            var result = _factory.CreateCommand();
  159
+            result.Connection = conn;
  160
+            result.CommandText = sql;
  161
+            if (args.Length > 0)
  162
+                result.AddParams(args);
  163
+            return result;
  164
+        }
  165
+        /// <summary>
  166
+        /// Returns and OpenConnection
  167
+        /// </summary>
  168
+        public virtual DbConnection OpenConnection() {
  169
+            var result = _factory.CreateConnection();
  170
+            result.ConnectionString = _connectionString;
  171
+            result.Open();
  172
+            return result;
  173
+        }
  174
+        /// <summary>
  175
+        /// Builds a set of Insert and Update commands based on the passed-on objects.
  176
+        /// These objects can be POCOs, Anonymous, NameValueCollections, or Expandos. Objects
  177
+        /// With a PK property (whatever PrimaryKeyField is set to) will be created at UPDATEs
  178
+        /// </summary>
  179
+        public virtual List<DbCommand> BuildCommands(params object[] things) {
  180
+            var commands = new List<DbCommand>();
  181
+            foreach (var item in things) {
  182
+                if (HasPrimaryKey(item)) {
  183
+                    commands.Add(CreateUpdateCommand(item, GetPrimaryKey(item)));
  184
+                } else {
  185
+                    commands.Add(CreateInsertCommand(item));
  186
+                }
  187
+            }
  188
+
  189
+            return commands;
  190
+        }
  191
+        /// <summary>
  192
+        /// Executes a set of objects as Insert or Update commands based on their property settings, within a transaction.
  193
+        /// These objects can be POCOs, Anonymous, NameValueCollections, or Expandos. Objects
  194
+        /// With a PK property (whatever PrimaryKeyField is set to) will be created at UPDATEs
  195
+        /// </summary>
  196
+        public virtual int Save(params object[] things) {
  197
+            var commands = BuildCommands(things);
  198
+            return Execute(commands);
  199
+        }
  200
+        public virtual int Execute(DbCommand command) {
  201
+            return Execute(new DbCommand[] { command });
  202
+        }
  203
+        /// <summary>
  204
+        /// Executes a series of DBCommands in a transaction
  205
+        /// </summary>
  206
+        public virtual int Execute(IEnumerable<DbCommand> commands) {
  207
+            var result = 0;
  208
+            using (var conn = OpenConnection()) {
  209
+                using (var tx = conn.BeginTransaction()) {
  210
+                    foreach (var cmd in commands) {
  211
+                        cmd.Connection = conn;
  212
+                        cmd.Transaction = tx;
  213
+                        result += cmd.ExecuteNonQuery();
  214
+                    }
  215
+                    tx.Commit();
  216
+                }
  217
+            }
  218
+            return result;
  219
+        }
  220
+        public virtual string PrimaryKeyField { get; set; }
  221
+        /// <summary>
  222
+        /// Conventionally introspects the object passed in for a field that 
  223
+        /// looks like a PK. If you've named your PrimaryKeyField, this becomes easy
  224
+        /// </summary>
  225
+        public virtual bool HasPrimaryKey(object o) {
  226
+            return o.ToDictionary().ContainsKey(PrimaryKeyField);
  227
+        }
  228
+        /// <summary>
  229
+        /// If the object passed in has a property with the same name as your PrimaryKeyField
  230
+        /// it is returned here.
  231
+        /// </summary>
  232
+        public virtual object GetPrimaryKey(object o) {
  233
+            object result = null;
  234
+            o.ToDictionary().TryGetValue(PrimaryKeyField, out result);
  235
+            return result;
  236
+        }
  237
+        public virtual string TableName { get; set; }
  238
+        /// <summary>
  239
+        /// Creates a command for use with transactions - internal stuff mostly, but here for you to play with
  240
+        /// </summary>
  241
+        public virtual DbCommand CreateInsertCommand(object o) {
  242
+            DbCommand result = null;
  243
+            var expando = o.ToExpando();
  244
+            var settings = (IDictionary<string, object>)expando;
  245
+            var sbKeys = new StringBuilder();
  246
+            var sbVals = new StringBuilder();
  247
+            var stub = "INSERT INTO {0} ({1}) \r\n VALUES ({2})";
  248
+            result = CreateCommand(stub, null);
  249
+            int counter = 0;
  250
+            foreach (var item in settings) {
  251
+                sbKeys.AppendFormat("{0},", item.Key);
  252
+                sbVals.AppendFormat("@{0},", counter.ToString());
  253
+                result.AddParam(item.Value);
  254
+                counter++;
  255
+            }
  256
+            if (counter > 0) {
  257
+                var keys = sbKeys.ToString().Substring(0, sbKeys.Length - 1);
  258
+                var vals = sbVals.ToString().Substring(0, sbVals.Length - 1);
  259
+                var sql = string.Format(stub, TableName, keys, vals);
  260
+                result.CommandText = sql;
  261
+            } else throw new InvalidOperationException("Can't parse this object to the database - there are no properties set");
  262
+            return result;
  263
+        }
  264
+        /// <summary>
  265
+        /// Creates a command for use with transactions - internal stuff mostly, but here for you to play with
  266
+        /// </summary>
  267
+        public virtual DbCommand CreateUpdateCommand(object o, object key) {
  268
+            var expando = o.ToExpando();
  269
+            var settings = (IDictionary<string, object>)expando;
  270
+            var sbKeys = new StringBuilder();
  271
+            var stub = "UPDATE {0} SET {1} WHERE {2} = @{3}";
  272
+            var args = new List<object>();
  273
+            var result = CreateCommand(stub, null);
  274
+            int counter = 0;
  275
+            foreach (var item in settings) {
  276
+                var val = item.Value;
  277
+                if (!item.Key.Equals(PrimaryKeyField, StringComparison.CurrentCultureIgnoreCase) && item.Value != null) {
  278
+                    result.AddParam(val);
  279
+                    sbKeys.AppendFormat("{0} = @{1}, \r\n", item.Key, counter.ToString());
  280
+                    counter++;
  281
+                }
  282
+            }
  283
+            if (counter > 0) {
  284
+                //add the key
  285
+                result.AddParam(key);
  286
+                //strip the last commas
  287
+                var keys = sbKeys.ToString().Substring(0, sbKeys.Length - 4);
  288
+                result.CommandText = string.Format(stub, TableName, keys, PrimaryKeyField, counter);
  289
+            } else throw new InvalidOperationException("No parsable object was sent in - could not divine any name/value pairs");
  290
+            return result;
  291
+        }
  292
+        /// <summary>
  293
+        /// Removes one or more records from the DB according to the passed-in WHERE
  294
+        /// </summary>
  295
+        public virtual DbCommand CreateDeleteCommand(string where = "", object key = null, params object[] args) {
  296
+            var sql = string.Format("DELETE FROM {0} ", TableName);
  297
+            if (key != null) {
  298
+                sql += string.Format("WHERE {0}=@0", PrimaryKeyField);
  299
+                args = new object[] { key };
  300
+            } else if (!string.IsNullOrEmpty(where)) {
  301
+                sql += where.Trim().StartsWith("where", StringComparison.CurrentCultureIgnoreCase) ? where : "WHERE " + where;
  302
+            }
  303
+            return CreateCommand(sql, null, args);
  304
+        }
  305
+        /// <summary>
  306
+        /// Adds a record to the database. You can pass in an Anonymous object, an ExpandoObject,
  307
+        /// A regular old POCO, or a NameValueColletion from a Request.Form or Request.QueryString
  308
+        /// </summary>
  309
+        public virtual object Insert(object o) {
  310
+            dynamic result = 0;
  311
+            using (var conn = OpenConnection()) {
  312
+                var cmd = CreateInsertCommand(o);
  313
+                cmd.Connection = conn;
  314
+                cmd.ExecuteNonQuery();
  315
+                cmd.CommandText = "SELECT @@IDENTITY as newID";
  316
+                result = cmd.ExecuteScalar();
  317
+            }
  318
+            return result;
  319
+        }
  320
+        /// <summary>
  321
+        /// Updates a record in the database. You can pass in an Anonymous object, an ExpandoObject,
  322
+        /// A regular old POCO, or a NameValueCollection from a Request.Form or Request.QueryString
  323
+        /// </summary>
  324
+        public virtual int Update(object o, object key) {
  325
+            return Execute(CreateUpdateCommand(o, key));
  326
+        }
  327
+        /// <summary>
  328
+        /// Removes one or more records from the DB according to the passed-in WHERE
  329
+        /// </summary>
  330
+        public int Delete(object key = null, string where = "", params object[] args) {
  331
+            return Execute(CreateDeleteCommand(where: where, key: key, args: args));
  332
+        }
  333
+        
  334
+        
  335
+        /// <summary>
  336
+        /// Returns all records complying with the passed-in WHERE clause and arguments, 
  337
+        /// ordered as specified, limited (TOP) by limit.
  338
+        /// </summary>
  339
+        public virtual IEnumerable<dynamic> All(string where = "", string orderBy = "", int limit = 0, string columns = "*", params object[] args) {
  340
+            string sql = BuildSelect(where, orderBy, limit);
  341
+            return Query(string.Format(sql, columns, TableName), args);
  342
+        }
  343
+
  344
+        private static string BuildSelect(string where, string orderBy, int limit) {
  345
+            string sql = limit > 0 ? "SELECT TOP " + limit + " {0} FROM {1} " : "SELECT {0} FROM {1} ";
  346
+            if (!string.IsNullOrEmpty(where))
  347
+                sql += where.Trim().StartsWith("where", StringComparison.CurrentCultureIgnoreCase) ? where : "WHERE " + where;
  348
+            if (!String.IsNullOrEmpty(orderBy))
  349
+                sql += orderBy.Trim().StartsWith("order by", StringComparison.CurrentCultureIgnoreCase) ? orderBy : " ORDER BY " + orderBy;
  350
+            return sql;
  351
+        }
  352
+        /// <summary>
  353
+        /// Returns all records complying with the passed-in WHERE clause and arguments, 
  354
+        /// ordered as specified, limited (TOP) by limit.
  355
+        /// </summary>
  356
+        public virtual void AllAsync(Action<List<dynamic>> callback,string where = "", string orderBy = "", int limit = 0, string columns = "*", params object[] args) {
  357
+            string sql = BuildSelect(where, orderBy, limit);
  358
+            QueryAsync(string.Format(sql, columns, TableName),callback, args);
  359
+        }
  360
+        /// <summary>
  361
+        /// Returns a dynamic PagedResult. Result properties are Items, TotalPages, and TotalRecords.
  362
+        /// </summary>
  363
+        public virtual dynamic Paged(string where = "", string orderBy = "", string columns = "*", int pageSize = 20, int currentPage = 1, params object[] args) {
  364
+            dynamic result = new ExpandoObject();
  365
+            var countSQL = string.Format("SELECT COUNT({0}) FROM {1}", PrimaryKeyField, TableName);
  366
+            if (String.IsNullOrEmpty(orderBy))
  367
+                orderBy = PrimaryKeyField;
  368
+
  369
+            if (!string.IsNullOrEmpty(where)) {
  370
+                if (!where.Trim().StartsWith("where", StringComparison.CurrentCultureIgnoreCase)) {
  371
+                    where = "WHERE " + where;
  372
+                }
  373
+            }
  374
+            var sql = 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);
  375
+            var pageStart = (currentPage - 1) * pageSize;
  376
+            sql += string.Format(" WHERE Row > {0} AND Row <={1}", pageStart, (pageStart + pageSize));
  377
+            countSQL += where;
  378
+            result.TotalRecords = Scalar(countSQL, args);
  379
+            result.TotalPages = result.TotalRecords / pageSize;
  380
+            if (result.TotalRecords % pageSize > 0)
  381
+                result.TotalPages += 1;
  382
+            result.Items = Query(string.Format(sql, columns, TableName), args);
  383
+            return result;
  384
+        }
  385
+        /// <summary>
  386
+        /// Returns a single row from the database
  387
+        /// </summary>
  388
+        public virtual dynamic Single(object key, string columns = "*") {
  389
+            var sql = string.Format("SELECT {0} FROM {1} WHERE {2} = @0", columns, TableName, PrimaryKeyField);
  390
+            var items = Query(sql, key).ToList();
  391
+            return items.FirstOrDefault();
  392
+        }
  393
+
  394
+        /// <summary>
  395
+        /// A little Rails-y love for ya
  396
+        /// </summary>
  397
+        public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) {
  398
+            //parse the method
  399
+            var stems = binder.Name.Split('_');
  400
+            var sb = new List<string>();
  401
+
  402
+            //first should be "FindBy or whatever"
  403
+            var op = stems[0];
  404
+            var counter = 0;
  405
+            for (int i = 1; i < stems.Length; i++) {
  406
+                if (stems[i].Trim().ToLower() != "and") {
  407
+                    sb.Add(stems[i] + "=@" + counter);
  408
+                    counter++;
  409
+                }
  410
+            }
  411
+            var sql = "SELECT * FROM " + TableName + " WHERE " + string.Join(" AND ", sb.ToArray());
  412
+            result = Query(sql, args);
  413
+            return true;
  414
+        }
  415
+    }
364 416
 }

0 notes on commit bdfd2a2

Please sign in to comment.
Something went wrong with that request. Please try again.