Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Major refactor... #11

Closed
wants to merge 16 commits into from

2 participants

@Talljoe

(edit: No more separation of concerns. But a lot of refactoring and additional work has been added.)
I refactored the code into three main concerns: DynamicConnection, DynamicDatabase, and DynamicModel. Even though it was completely possible, it seemed weird to me to take commands built from separate DynamicModel instances and pass them to the "Execute" method of some arbitrary one. So I pulled out a DynamicDatabase and moved the generic sql="" methods there. That created a problem with Insert() because it used special semantics, so I split off a DynamicConnection object that encapsulates everything into a single connection (with the exception of Query() and Fetch()). The DynamicConnection is hidden behind the IDynamicDatabase interface so it is interchangeable with DynamicDatabase (DynamicModel also implements IDynamicDatabase methods to retain backwards compatibility, although I would like to see them deprecated).

This breaks backwards compatibility by changing the return values for the BuildXxxCommand methods. This won't affect anyone that uses "var" and only passes these to DynamicModel.

This is built off of the previous code change I added to support whitelists.

Linecount: 321 (-41 from my previous change which was -13 from robconery/massive)

Joe Wasson added some commits
Joe Wasson Minor cleanup. bb3c6df
Joe Wasson Add ability to specify a whitelist on insert and update.
Note: This code also changes functionality such that Update can't update the primary key.  I feel this is a good thing.
7f04b08
Joe Wasson Share object-based column specifications with query operations. e66c968
Joe Wasson Maybe keeping backwards compatibility is better. Adding back ability …
…to set primary key during insert.
f611a68
Joe Wasson Refactor code to split out model from the database and have a separat…
…e class for the connection.
82ac76f
Joe Wasson DbDataReader should be disposed. ac987d8
Joe Wasson Minor cleanup and removal of dead code. f74e935
Joe Wasson Remove the DynamicConnection work. Really isn't needed for Massive an…
…d there aren't a lot of compelling uses outside of Massive.
948dabd
Joe Wasson Remove basic sql methods from the Model, can get to the via the .Data…
…base property now.

Note: This breaks backwards compatibility.
17d065c
Joe Wasson Create a specific class for Paged() so that it's easier to discover. 6c12173
@Talljoe

I re-refactored to remove the DynamicConnection, since it really isn't needed. I also went ahead and broke backwards compatibility by fully removing the basic sql operations from Dynamic Model.

Linecount: 294.

@robconery
Collaborator

Hi Joe - thanks for the work - I appreciate where you're going with this but it's a bit more change than I'd like. For instance - I don't want a class for Paged; it's dynamic - that's the point (you can change what you like, when you like, as needed).

To me, this is a utility class - not a framework. I don't want to wrap concepts into classes (DynamicTable, DynamicDatabase, etc) that interoperate. I want to stay close to the metal.

I appreciate refactorings - so I might cherry pick a few things you've done.

@Talljoe

Fair enough. I can appreciate wanting to keep it a simple utility class.

Most of the changes I made were born out my usages of the class. For instance, I changed Paged() because every time I went to use it I did it wrong. :) And splitting the database from the the model-specific work made sense to me:
var id = db.GetTable(Group.TableName).Insert(saveModel, typeof(IGroupWhitelist));
db.GetTable(GroupTier.TableName).Insert(new { GroupId = id, model.MemberCount, model.BeginDate, });

Thanks for Massive. I procrastinated for months trying to avoid data access; now I'm getting it done!

@Talljoe

Here is the refactor re-refactored into a single class again with DynamicPagedResult removed. Should feel more like home to you. :) Should also make it easier to cherry pick the changes you want.

This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Feb 22, 2011
  1. Minor cleanup.

    Joe Wasson authored
  2. Add ability to specify a whitelist on insert and update.

    Joe Wasson authored
    Note: This code also changes functionality such that Update can't update the primary key.  I feel this is a good thing.
  3. Maybe keeping backwards compatibility is better. Adding back ability …

    Joe Wasson authored
    …to set primary key during insert.
  4. Refactor code to split out model from the database and have a separat…

    Joe Wasson authored
    …e class for the connection.
  5. DbDataReader should be disposed.

    Joe Wasson authored
  6. Minor cleanup and removal of dead code.

    Joe Wasson authored
Commits on Feb 27, 2011
  1. Remove the DynamicConnection work. Really isn't needed for Massive an…

    Joe Wasson authored
    …d there aren't a lot of compelling uses outside of Massive.
  2. Remove basic sql methods from the Model, can get to the via the .Data…

    Joe Wasson authored
    …base property now.
    
    Note: This breaks backwards compatibility.
  3. Update docs in README.

    Joe Wasson authored
Commits on Feb 28, 2011
  1. Really wanted a simple "Execute(sql, args)" method back. Also made th…

    Joe Wasson authored
    …e transaction more explicit.
  2. Remove DynamicCommand and DynamicPagedResult.

    Joe Wasson authored
  3. Collapse DynamicDatabase back into DynamicModel.

    Joe Wasson authored
Commits on Mar 1, 2011
This page is out of date. Refresh to see the latest.
Showing with 161 additions and 263 deletions.
  1. +160 −261 Massive.cs
  2. +1 −2  README.markdown
View
421 Massive.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
@@ -6,23 +6,15 @@
using System.Data.Common;
using System.Dynamic;
using System.Linq;
-using System.Text;
-using System.Collections;
+using System.Reflection;
using System.Text.RegularExpressions;
-
namespace Massive {
public static class ObjectExtensions {
- /// <summary>
- /// Extension method for adding in a bunch of parameters
- /// </summary>
- public static void AddParams(this DbCommand cmd, object[] args) {
- foreach (var item in args) {
- AddParam(cmd, item);
- }
- }
- /// <summary>
- /// Extension for adding single parameter
- /// </summary>
+ /// <summary> Extension method for performing an action against all elements. </summary>
+ public static void ForEach<T>(this IEnumerable<T> collection, Action<T> action) { foreach (var item in collection) action(item); }
+ /// <summary> Extension method for adding in a bunch of parameters </summary>
+ public static void AddParams(this DbCommand cmd, IEnumerable<object> args) { (args ?? Enumerable.Empty<object>()).ForEach(item => AddParam(cmd, item)); }
+ /// <summary> Extension for adding single parameter </summary>
public static void AddParam(this DbCommand cmd, object item) {
var p = cmd.CreateParameter();
p.ParameterName = string.Format("@{0}", cmd.Parameters.Count);
@@ -45,113 +37,88 @@ public static class ObjectExtensions {
}
cmd.Parameters.Add(p);
}
- /// <summary>
- /// Turns an IDataReader to a Dynamic list of things
- /// </summary>
- public static List<dynamic> ToExpandoList(this IDataReader rdr) {
- var result = new List<dynamic>();
- while (rdr.Read()) {
- dynamic e = new ExpandoObject();
- var d = e as IDictionary<string, object>;
- for (int i = 0; i < rdr.FieldCount; i++)
- d.Add(rdr.GetName(i), rdr[i]);
- result.Add(e);
- }
- return result;
- }
- /// <summary>
- /// Turns the object into an ExpandoObject
- /// </summary>
+ /// <summary> Turns the object into an ExpandoObject </summary>
public static dynamic ToExpando(this object o) {
+ if (o is ExpandoObject) return o; //shouldn't have to... but just in case
var result = new ExpandoObject();
var d = result as IDictionary<string, object>; //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)) {
- var nv = (NameValueCollection)o;
- nv.Cast<string>().Select(key => new KeyValuePair<string, object>(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));
- }
- }
+ var nv = o as NameValueCollection;
+ if (nv != null) nv.Cast<string>().Select(key => new KeyValuePair<string, object>(key, nv[key])).ForEach(d.Add);
+ else o.GetType().GetProperties().ForEach(item => d.Add(item.Name, item.GetValue(o, null)));
return result;
}
- /// <summary>
- /// Turns the object into a Dictionary
- /// </summary>
- public static IDictionary<string, object> ToDictionary(this object thingy) {
- return (IDictionary<string, object>)thingy.ToExpando();
- }
+ /// <summary> Turns the object into a Dictionary </summary>
+ public static IDictionary<string, object> ToDictionary(this object thingy) { return (thingy as IDictionary<string, object>) ?? thingy.ToExpando(); }
}
- /// <summary>
- /// A class that wraps your database table in Dynamic Funtime
- /// </summary>
- public class DynamicModel {
- DbProviderFactory _factory;
- string _connectionString;
-
- public DynamicModel(string connectionStringName= "", string tableName = "", string primaryKeyField ="") {
- TableName = tableName == "" ? this.GetType().Name : tableName;
- PrimaryKeyField = string.IsNullOrEmpty(primaryKeyField) ? "ID" : primaryKeyField;
- if (connectionStringName == "")
- connectionStringName = ConfigurationManager.ConnectionStrings[0].Name;
+ /// <summary> A class that wraps your database in Dynamic Funtime </summary>
+ public class DynamicModel {
+ readonly DbProviderFactory _factory;
+ readonly string _connectionString;
+ public DynamicModel(string connectionStringName = "", string tableName = "", string primaryKeyField = "") {
var _providerName = "System.Data.SqlClient";
if (ConfigurationManager.ConnectionStrings[connectionStringName] != null) {
if (!string.IsNullOrEmpty(ConfigurationManager.ConnectionStrings[connectionStringName].ProviderName))
_providerName = ConfigurationManager.ConnectionStrings[connectionStringName].ProviderName;
- } else {
- throw new InvalidOperationException("Can't find a connection string with the name '" + connectionStringName + "'");
- }
+ } else throw new InvalidOperationException("Can't find a connection string with the name '" + connectionStringName + "'");
_factory = DbProviderFactories.GetFactory(_providerName);
_connectionString = ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString;
+ TableName = tableName == "" ? this.GetType().Name : tableName;
+ PrimaryKeyField = string.IsNullOrEmpty(primaryKeyField) ? "ID" : primaryKeyField;
}
- /// <summary>
- /// Enumerates the reader yielding the result - thanks to Jeroen Haegebaert
- /// </summary>
- public IEnumerable<dynamic> Query(string sql, params object[] args) {
- using (var conn = OpenConnection()) {
- var rdr = CreateCommand(sql, conn, args).ExecuteReader(CommandBehavior.CloseConnection);
- while (rdr.Read()) {
- var e = new ExpandoObject();
- var d = e as IDictionary<string, object>;
- for (var i = 0; i < rdr.FieldCount; i++)
- d.Add(rdr.GetName(i), rdr[i]);
- yield return e;
+ /// <summary> Gets or sets the primary key for this model. </summary>
+ public string PrimaryKeyField { get; set; }
+ /// <summary> Gets or sets the table name for this model. </summary>
+ public string TableName { get; set; }
+ /// <summary> Creates a DBCommand that you can use for loving your database. </summary>
+ public DbCommand CreateCommand(string sql, DbConnection connection = null, object[] args = null) {
+ var result = _factory.CreateCommand();
+ result.CommandText = sql;
+ result.AddParams(args);
+ result.Connection = connection;
+ return result;
+ }
+ /// <summary> Enumerates the reader yielding the result - thanks to Jeroen Haegebaert </summary>
+ public IEnumerable<dynamic> Query(string sql, params object[] args) { return Query(CreateCommand(sql, args: args)); }
+ private IEnumerable<dynamic> Query(DbCommand command) {
+ using(var conn = OpenConnection()) {
+ command.Connection = conn;
+ using (var rdr = command.ExecuteReader(CommandBehavior.CloseConnection)) {
+ while (rdr.Read()) {
+ var d = (IDictionary<string, object>)new ExpandoObject();
+ for (var i = 0; i < rdr.FieldCount; i++) d.Add(rdr.GetName(i), rdr[i]);
+ yield return d;
+ }
}
}
}
- /// <summary>
- /// Runs a query against the database
- /// </summary>
- public IList<dynamic> Fetch(string sql, params object[] args) {
- return Query(sql, args).ToList<dynamic>();
- }
- /// <summary>
- /// Returns a single result
- /// </summary>
- public object Scalar(string sql, params object[] args) {
- object result = null;
+ /// <summary> Runs a query against the database. </summary>
+ public IList<dynamic> Fetch(string sql, params object[] args) { return Query(sql, args).ToList(); }
+ /// <summary> Returns a single result </summary>
+ public object Scalar(string sql, params object[] args) { return Scalar(CreateCommand(sql, args: args)); }
+ private object Scalar(DbCommand command) {
using (var conn = OpenConnection()) {
- result = CreateCommand(sql, conn, args).ExecuteScalar();
+ command.Connection = conn;
+ return command.ExecuteScalar();
}
- return result;
- }
- /// <summary>
- /// Creates a DBCommand that you can use for loving your database.
- /// </summary>
- DbCommand CreateCommand(string sql, DbConnection conn, params object[] args) {
- DbCommand result = null;
- result = _factory.CreateCommand();
- result.Connection = conn;
- result.CommandText = sql;
- if (args.Length > 0)
- result.AddParams(args);
- return result;
}
- /// <summary>
- /// Returns and OpenConnection
- /// </summary>
+ /// <summary> Executes a single DBCommand </summary>
+ public int Execute(string sql, params object[] args) { return this.Execute(new [] {CreateCommand(sql, args: args)}, transaction: false); }
+ /// <summary> Executes a series of DBCommands in a transaction </summary>
+ public int Execute(params DbCommand[] commands) { return this.Execute(commands, transaction: true); }
+ /// <summary> Executes a series of DBCommands optionally in a transaction </summary>
+ public int Execute(IEnumerable<DbCommand> commands, bool transaction = true) {
+ using(var connection = OpenConnection())
+ using(var tx = (transaction) ? connection.BeginTransaction() : null) {
+ var result = commands.Aggregate(0, (a, cmd) => {
+ cmd.Connection = connection;
+ cmd.Transaction = tx;
+ return a + cmd.ExecuteNonQuery();
+ });
+ if(tx != null) tx.Commit();
+ return result;
+ }
+ }
+ /// <summary> Returns a dynamic database scoped to a single connection. </summary>
public DbConnection OpenConnection() {
var conn = _factory.CreateConnection();
conn.ConnectionString = _connectionString;
@@ -163,205 +130,137 @@ public class DynamicModel {
/// These objects can be POCOs, Anonymous, NameValueCollections, or Expandos. Objects
/// With a PK property (whatever PrimaryKeyField is set to) will be created at UPDATEs
/// </summary>
- public List<DbCommand> BuildCommands(params object[] things) {
- var commands = new List<DbCommand>();
- foreach (var item in things) {
- if (HasPrimaryKey(item)) {
- commands.Add(CreateUpdateCommand(item,GetPrimaryKey(item)));
- }else{
- commands.Add(CreateInsertCommand(item));
- }
- }
-
- return commands;
+ public List<DbCommand> BuildCommands(params object[] things) { return BuildCommandsWithWhitelist(null, things); }
+ /// <summary>
+ /// 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
+ /// </summary>
+ public List<DbCommand> BuildCommandsWithWhitelist(object whitelist, params object[] things) {
+ return things.Select(item => HasPrimaryKey(item) ? CreateUpdateCommand(item,GetPrimaryKey(item),whitelist) : CreateInsertCommand(item,whitelist)).ToList();
}
/// <summary>
/// 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
/// </summary>
- public int Save(params object[] things) {
- var commands = BuildCommands(things);
- return Execute(commands);
- }
- public int Execute(DbCommand command) {
- return Execute(new DbCommand[] { command });
- }
+ public int Save(params object[] things) { return SaveWithWhitelist(null, things); }
/// <summary>
- /// Executes a series of DBCommands in a transaction
+ /// 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
/// </summary>
- public int Execute(IEnumerable<DbCommand> 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 int SaveWithWhitelist(object whitelist, params object[] things) {
+ return Execute(BuildCommandsWithWhitelist(whitelist, things));
}
- public string PrimaryKeyField { get; set; }
/// <summary>
/// Conventionally introspects the object passed in for a field that
/// looks like a PK. If you've named your PrimaryKeyField, this becomes easy
/// </summary>
- public bool HasPrimaryKey(object o) {
- return o.ToDictionary().ContainsKey(PrimaryKeyField);
- }
+ public bool HasPrimaryKey(object o) { return o.ToDictionary().ContainsKey(PrimaryKeyField); }
/// <summary>
/// If the object passed in has a property with the same name as your PrimaryKeyField
/// it is returned here.
/// </summary>
public object GetPrimaryKey(object o) {
- object result = null;
+ object result;
o.ToDictionary().TryGetValue(PrimaryKeyField, out result);
return result;
}
- public string TableName { get; set; }
- /// <summary>
- /// Creates a command for use with transactions - internal stuff mostly, but here for you to play with
- /// </summary>
- public DbCommand CreateInsertCommand(object o) {
- DbCommand result = null;
- var expando = o.ToExpando();
- var settings = (IDictionary<string, object>)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++;
+ /// <summary> Creates a command for use with transactions - internal stuff mostly, but here for you to play with </summary>
+ public DbCommand CreateInsertCommand(object o, object whitelist = null) {
+ const string stub = "INSERT INTO {0} ({1}) \r\n VALUES ({2}); SELECT @@IDENTITY AS NewID";
+ var items = FilterItems(o, whitelist).ToList();
+ if (items.Any()) {
+ var keys = string.Join(",", items.Select(item => item.Key));
+ var vals = string.Join(",", items.Select((_, i) => "@" + i.ToString()));
+ return CreateCommand(string.Format(stub, TableName, keys, vals), args: items.Select(item => item.Value).ToArray());
}
- 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;
+ throw new InvalidOperationException("Can't parse this object to the database - there are no properties set");
}
- /// <summary>
- /// Creates a command for use with transactions - internal stuff mostly, but here for you to play with
- /// </summary>
- public DbCommand CreateUpdateCommand(object o, object key) {
- var expando = o.ToExpando();
- var settings = (IDictionary<string, object>)expando;
- var sbKeys = new StringBuilder();
- var stub = "UPDATE {0} SET {1} WHERE {2} = @{3}";
- var args = new List<object>();
- 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++;
- }
+ /// <summary> Creates a command for use with transactions - internal stuff mostly, but here for you to play with </summary>
+ public DbCommand CreateUpdateCommand(object o, object key, object whitelist = null) {
+ const string stub = "UPDATE {0} SET {1} WHERE {2} = @{3}";
+ var items = FilterItems(o, whitelist).Where(item => !item.Key.Equals(PrimaryKeyField, StringComparison.CurrentCultureIgnoreCase) && item.Value != null).ToList();
+ if (items.Any()) {
+ var keys = string.Join(",", items.Select((item, i) => string.Format("{0} = @{1} \r\n", item.Key, i)));
+ return CreateCommand(string.Format(stub, TableName, keys, PrimaryKeyField, items.Count),
+ args: items.Select(item => item.Value).Concat(new[] {key}).ToArray());
}
- 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;
+ throw new InvalidOperationException("No parsable object was sent in - could not divine any name/value pairs");
}
- /// <summary>
- /// Removes one or more records from the DB according to the passed-in WHERE
- /// </summary>
- public 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);
+ private static IEnumerable<KeyValuePair<string,object>> FilterItems(object o, object whitelist) {
+ IEnumerable<KeyValuePair<string, object>> settings = o.ToDictionary();
+ var whitelistValues = GetColumns(whitelist).Select(s => s.Trim());
+ if (!string.Equals("*", whitelistValues.FirstOrDefault(), StringComparison.Ordinal))
+ settings = settings.Join(whitelistValues, s => s.Key.Trim(), w => w, (s,_) => s, StringComparer.OrdinalIgnoreCase);
+ return settings;
+ }
+ private static IEnumerable<string> GetColumns(object columns) {
+ return (columns == null) ? new[]{"*"} :
+ (columns is string) ? ((string)columns).Split(new[]{','}, StringSplitOptions.RemoveEmptyEntries) :
+ (columns is Type) ? ((Type)columns).GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance).Select(prop => prop.Name)
+ : (columns as IEnumerable<string>) ?? columns.ToDictionary().Select(kvp => kvp.Key);
+ }
+ private DbCommand BuildCommand(string sql, object key = null, object where = null, params object[] args) {
+ var command = CreateCommand(sql);
+ if (key != null) where = new Dictionary<string, object> {{PrimaryKeyField, key}};
+ if(where == null) return command;
+ var whereString = where as string;
+ if (whereString != null) {
+ var whereRegex = new Regex(@"^where ", RegexOptions.IgnoreCase);
+ var keyword = whereRegex.IsMatch(sql.Trim()) ? " AND " : " WHERE ";
+ command.CommandText += keyword + whereString.Replace(whereString.Trim(), String.Empty);
+ command.AddParams(args);
+ } else {
+ var dict = where.ToDictionary();
+ command.CommandText += " WHERE " + String.Join(" AND ", dict.Select((kvp, i) => String.Format("{0} = @{1}", kvp.Key, i)));
+ command.AddParams(dict.Select(kvp => kvp.Value));
+ }
+ return command;
+ }
+ /// <summary> Removes one or more records from the DB according to the passed-in WHERE </summary>
+ public DbCommand CreateDeleteCommand(object key = null, object where = null, params object[] args) {
+ return BuildCommand(string.Format("DELETE FROM {0}", TableName), key, where, args);
}
/// <summary>
/// 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
/// </summary>
- public object Insert(object o) {
- dynamic result = 0;
- using (var conn = OpenConnection()) {
- var cmd = CreateInsertCommand(o);
- cmd.Connection = conn;
- cmd.ExecuteNonQuery();
- cmd.CommandText = "SELECT @@IDENTITY as newID";
- result = cmd.ExecuteScalar();
- }
- return result;
- }
+ public object Insert(object o, object whitelist = null) { return Scalar(CreateInsertCommand(o, whitelist)); }
/// <summary>
/// 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
/// </summary>
- public int Update(object o, object key) {
- return Execute(CreateUpdateCommand(o, key));
- }
- /// <summary>
- /// Removes one or more records from the DB according to the passed-in WHERE
- /// </summary>
- public int Delete(object key = null, string where = "", params object[] args) {
- return Execute(CreateDeleteCommand(where: where, key:key, args: args));
- }
+ public int Update(object o, object key, object whitelist = null) { return Execute(CreateUpdateCommand(o, key, whitelist)); }
+ /// <summary> Removes one or more records from the DB according to the passed-in WHERE </summary>
+ public int Delete(object key = null, object where = null, params object[] args) { return Execute(CreateDeleteCommand(key, where, args)); }
/// <summary>
- /// Returns all records complying with the passed-in WHERE clause and arguments,
- /// ordered as specified, limited (TOP) by limit.
+ /// Returns all records complying with the passed-in WHERE clause and arguments, ordered as specified, limited (TOP) by limit.
/// </summary>
- public IEnumerable<dynamic> All(string where = "", string orderBy = "", int limit = 0, string columns = "*", params object[] args) {
- 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;
+ public IEnumerable<dynamic> All(object where = null, string orderBy = "", int limit = 0, object columns = null, params object[] args) {
+ var sql = String.Format(limit > 0 ? "SELECT TOP " + limit + " {0} FROM {1}" : "SELECT {0} FROM {1}", String.Join(",", GetColumns(columns)), TableName);
+ var command = BuildCommand(sql, where: where, args: args);
if (!String.IsNullOrEmpty(orderBy))
- sql += orderBy.Trim().StartsWith("order by", StringComparison.CurrentCultureIgnoreCase) ? orderBy : " ORDER BY " + orderBy;
- return Query(string.Format(sql, columns,TableName), args);
+ command.CommandText += (orderBy.Trim().StartsWith("order by", StringComparison.CurrentCultureIgnoreCase) ? " " : " ORDER BY ") + orderBy;
+ return Query(command);
}
-
- /// <summary>
- /// Returns a dynamic PagedResult. Result properties are Items, TotalPages, and TotalRecords.
- /// </summary>
- public 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;
- var sql = string.Format("SELECT {0} FROM (SELECT ROW_NUMBER() OVER (ORDER BY {2}) AS Row, {0} FROM {3}) AS Paged ",columns,pageSize,orderBy,TableName);
+ /// <summary> Returns a dynamic PagedResult. Result properties are Items, TotalPages, and TotalRecords. </summary>
+ public dynamic Paged(object where = null, string orderBy = "", object columns = null, int pageSize = 20, int currentPage =1, params object[] args) {
+ var countSql = string.Format("SELECT COUNT({0}) FROM {1}", PrimaryKeyField, TableName);
+ if (String.IsNullOrEmpty(orderBy)) orderBy = PrimaryKeyField;
+ var sql = string.Format("SELECT {0} FROM (SELECT ROW_NUMBER() OVER (ORDER BY {1}) AS Row, {0} FROM {2}) AS Paged", string.Join(",", GetColumns(columns)), orderBy, TableName);
var pageStart = (currentPage -1) * pageSize;
sql+= string.Format(" WHERE Row >={0} AND Row <={1}",pageStart, (pageStart + pageSize));
- var pagedWhere = "";
- if (!string.IsNullOrEmpty(where)) {
- if (where.Trim().StartsWith("where", StringComparison.CurrentCultureIgnoreCase)) {
- pagedWhere = Regex.Replace(where, "where ", "and ", RegexOptions.IgnoreCase);
- }
- }
- sql += pagedWhere;
- 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;
+ var queryCommand = BuildCommand(sql, where: where, args: args);
+ var whereCommand = BuildCommand(countSql, where: where, args: args);
+ var totalRecords = (int)Scalar(whereCommand);
+ return new { TotalRecords = totalRecords, TotalPages = (totalRecords + (pageSize - 1)) / pageSize, Items = Query(queryCommand) }.ToExpando();
}
- /// <summary>
- /// Returns a single row from the database
- /// </summary>
- public dynamic Single(object key, string columns = "*") {
- var sql = string.Format("SELECT {0} FROM {1} WHERE {2} = @0", columns,TableName, PrimaryKeyField);
- return Fetch(sql, key).FirstOrDefault();
+ /// <summary> Returns a single row from the database </summary>
+ public dynamic Single(object key = null, object where = null, object columns = null) {
+ var sql = string.Format("SELECT {0} FROM {1}", string.Join(",", GetColumns(columns)), TableName);
+ return Query(BuildCommand(sql, key, where)).FirstOrDefault();
}
}
}
View
3  README.markdown
@@ -3,8 +3,7 @@ Massive is a Single File Database Lover. Move over Bacon - Taste is got a new fr
I'm sharing this with the world because we need another way to access data - don't you think? Truthfully - I wanted to see if I could flex the C# 4 stuff and
-run up data access with a single file. I used to have this down to 350 lines, but you also needed to reference WebMatrix.Data. Now you don't - this is ready to roll
-and weighs in at a lovely 524 lines of code. Most of which is comments.
+run up data access with a single file. This is ready to roll and weighs in at a lovely 266 lines of code. Some of which is comments.
How To Install It?
------------------
Something went wrong with that request. Please try again.