PageRenderTime 54ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Roster/Roster/App_Code/Massive.cs

https://bitbucket.org/DefSol/roster
C# | 645 lines | 495 code | 26 blank | 124 comment | 104 complexity | f7d5cb720f59339b7d76b64f75c0880f MD5 | raw file
  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. using System.Text.RegularExpressions;
  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>;
  60. for (int i = 0; i < rdr.FieldCount; i++)
  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. /// Convenience class for opening/executing data
  91. /// </summary>
  92. public static class DB {
  93. public static DynamicModel Current {
  94. get {
  95. if (ConfigurationManager.ConnectionStrings.Count > 1) {
  96. return new DynamicModel(ConfigurationManager.ConnectionStrings[1].Name);
  97. }
  98. throw new InvalidOperationException("Need a connection string name - can't determine what it is");
  99. }
  100. }
  101. }
  102. /// <summary>
  103. /// A class that wraps your database table in Dynamic Funtime
  104. /// </summary>
  105. public class DynamicModel : DynamicObject {
  106. DbProviderFactory _factory;
  107. string ConnectionString;
  108. public static DynamicModel Open(string connectionStringName) {
  109. dynamic dm = new DynamicModel(connectionStringName);
  110. return dm;
  111. }
  112. public DynamicModel(string connectionStringName, string tableName = "",
  113. string primaryKeyField = "", string descriptorField = "") {
  114. TableName = tableName == "" ? this.GetType().Name : tableName;
  115. PrimaryKeyField = string.IsNullOrEmpty(primaryKeyField) ? "ID" : primaryKeyField;
  116. var _providerName = "System.Data.SqlClient";
  117. _factory = DbProviderFactories.GetFactory(_providerName);
  118. ConnectionString = ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString;
  119. }
  120. /// <summary>
  121. /// Creates a new Expando from a Form POST - white listed against the columns in the DB
  122. /// </summary>
  123. public dynamic CreateFrom(NameValueCollection coll) {
  124. dynamic result = new ExpandoObject();
  125. var dc = (IDictionary<string, object>)result;
  126. var schema = Schema;
  127. //loop the collection, setting only what's in the Schema
  128. foreach (var item in coll.Keys) {
  129. var exists = schema.Any(x => x.COLUMN_NAME.ToLower() == item.ToString().ToLower());
  130. if (exists) {
  131. var key = item.ToString();
  132. var val = coll[key];
  133. dc.Add(key, val);
  134. }
  135. }
  136. return result;
  137. }
  138. /// <summary>
  139. /// Gets a default value for the column
  140. /// </summary>
  141. public dynamic DefaultValue(dynamic column) {
  142. dynamic result = null;
  143. string def = column.COLUMN_DEFAULT;
  144. if (String.IsNullOrEmpty(def)) {
  145. result = null;
  146. } else if (def == "getdate()" || def == "(getdate())") {
  147. result = DateTime.Now.ToShortDateString();
  148. } else if (def == "newid()") {
  149. result = Guid.NewGuid().ToString();
  150. } else {
  151. result = def.Replace("(", "").Replace(")", "");
  152. }
  153. return result;
  154. }
  155. /// <summary>
  156. /// Creates an empty Expando set with defaults from the DB
  157. /// </summary>
  158. public dynamic Prototype {
  159. get {
  160. dynamic result = new ExpandoObject();
  161. var schema = Schema;
  162. foreach (dynamic column in schema) {
  163. var dc = (IDictionary<string, object>)result;
  164. dc.Add(column.COLUMN_NAME, DefaultValue(column));
  165. }
  166. result._Table = this;
  167. return result;
  168. }
  169. }
  170. private string _descriptorField;
  171. public string DescriptorField {
  172. get {
  173. return _descriptorField;
  174. }
  175. }
  176. /// <summary>
  177. /// List out all the schema bits for use with ... whatever
  178. /// </summary>
  179. IEnumerable<dynamic> _schema;
  180. public IEnumerable<dynamic> Schema {
  181. get {
  182. if (_schema == null)
  183. _schema = Query("SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = @0", TableName);
  184. return _schema;
  185. }
  186. }
  187. /// <summary>
  188. /// Enumerates the reader yielding the result - thanks to Jeroen Haegebaert
  189. /// </summary>
  190. public virtual IEnumerable<dynamic> Query(string sql, params object[] args) {
  191. using (var conn = OpenConnection()) {
  192. var rdr = CreateCommand(sql, conn, args).ExecuteReader();
  193. while (rdr.Read()) {
  194. yield return rdr.RecordToExpando(); ;
  195. }
  196. }
  197. }
  198. public virtual IEnumerable<dynamic> Query(string sql, DbConnection connection, params object[] args) {
  199. using (var rdr = CreateCommand(sql, connection, args).ExecuteReader()) {
  200. while (rdr.Read()) {
  201. yield return rdr.RecordToExpando(); ;
  202. }
  203. }
  204. }
  205. /// <summary>
  206. /// Returns a single result
  207. /// </summary>
  208. public virtual object Scalar(string sql, params object[] args) {
  209. object result = null;
  210. using (var conn = OpenConnection()) {
  211. result = CreateCommand(sql, conn, args).ExecuteScalar();
  212. }
  213. return result;
  214. }
  215. /// <summary>
  216. /// Creates a DBCommand that you can use for loving your database.
  217. /// </summary>
  218. DbCommand CreateCommand(string sql, DbConnection conn, params object[] args) {
  219. var result = _factory.CreateCommand();
  220. result.Connection = conn;
  221. result.CommandText = sql;
  222. if (args.Length > 0)
  223. result.AddParams(args);
  224. return result;
  225. }
  226. /// <summary>
  227. /// Returns and OpenConnection
  228. /// </summary>
  229. public virtual DbConnection OpenConnection() {
  230. var result = _factory.CreateConnection();
  231. result.ConnectionString = ConnectionString;
  232. result.Open();
  233. return result;
  234. }
  235. /// <summary>
  236. /// Builds a set of Insert and Update commands based on the passed-on objects.
  237. /// These objects can be POCOs, Anonymous, NameValueCollections, or Expandos. Objects
  238. /// With a PK property (whatever PrimaryKeyField is set to) will be created at UPDATEs
  239. /// </summary>
  240. public virtual List<DbCommand> BuildCommands(params object[] things) {
  241. var commands = new List<DbCommand>();
  242. foreach (var item in things) {
  243. if (HasPrimaryKey(item)) {
  244. commands.Add(CreateUpdateCommand(item, GetPrimaryKey(item)));
  245. } else {
  246. commands.Add(CreateInsertCommand(item));
  247. }
  248. }
  249. return commands;
  250. }
  251. public virtual int Execute(DbCommand command) {
  252. return Execute(new DbCommand[] { command });
  253. }
  254. public virtual int Execute(string sql, params object[] args) {
  255. return Execute(CreateCommand(sql, null, args));
  256. }
  257. /// <summary>
  258. /// Executes a series of DBCommands in a transaction
  259. /// </summary>
  260. public virtual int Execute(IEnumerable<DbCommand> commands) {
  261. var result = 0;
  262. using (var conn = OpenConnection()) {
  263. using (var tx = conn.BeginTransaction()) {
  264. foreach (var cmd in commands) {
  265. cmd.Connection = conn;
  266. cmd.Transaction = tx;
  267. result += cmd.ExecuteNonQuery();
  268. }
  269. tx.Commit();
  270. }
  271. }
  272. return result;
  273. }
  274. public virtual string PrimaryKeyField { get; set; }
  275. /// <summary>
  276. /// Conventionally introspects the object passed in for a field that
  277. /// looks like a PK. If you've named your PrimaryKeyField, this becomes easy
  278. /// </summary>
  279. public virtual bool HasPrimaryKey(object o) {
  280. return o.ToDictionary().ContainsKey(PrimaryKeyField);
  281. }
  282. /// <summary>
  283. /// If the object passed in has a property with the same name as your PrimaryKeyField
  284. /// it is returned here.
  285. /// </summary>
  286. public virtual object GetPrimaryKey(object o) {
  287. object result = null;
  288. o.ToDictionary().TryGetValue(PrimaryKeyField, out result);
  289. return result;
  290. }
  291. public virtual string TableName { get; set; }
  292. /// <summary>
  293. /// Returns all records complying with the passed-in WHERE clause and arguments,
  294. /// ordered as specified, limited (TOP) by limit.
  295. /// </summary>
  296. public virtual IEnumerable<dynamic> All(string where = "", string orderBy = "", int limit = 0, string columns = "*", params object[] args) {
  297. string sql = BuildSelect(where, orderBy, limit);
  298. return Query(string.Format(sql, columns, TableName), args);
  299. }
  300. private static string BuildSelect(string where, string orderBy, int limit) {
  301. string sql = limit > 0 ? "SELECT TOP " + limit + " {0} FROM {1} " : "SELECT {0} FROM {1} ";
  302. if (!string.IsNullOrEmpty(where))
  303. sql += where.Trim().StartsWith("where", StringComparison.OrdinalIgnoreCase) ? where : "WHERE " + where;
  304. if (!String.IsNullOrEmpty(orderBy))
  305. sql += orderBy.Trim().StartsWith("order by", StringComparison.OrdinalIgnoreCase) ? orderBy : " ORDER BY " + orderBy;
  306. return sql;
  307. }
  308. /// <summary>
  309. /// Returns a dynamic PagedResult. Result properties are Items, TotalPages, and TotalRecords.
  310. /// </summary>
  311. public virtual dynamic Paged(string where = "", string orderBy = "", string columns = "*", int pageSize = 20, int currentPage = 1, params object[] args) {
  312. dynamic result = new ExpandoObject();
  313. var countSQL = string.Format("SELECT COUNT({0}) FROM {1}", PrimaryKeyField, TableName);
  314. if (String.IsNullOrEmpty(orderBy))
  315. orderBy = PrimaryKeyField;
  316. if (!string.IsNullOrEmpty(where)) {
  317. if (!where.Trim().StartsWith("where", StringComparison.OrdinalIgnoreCase)) {
  318. where = "WHERE " + where;
  319. }
  320. }
  321. 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);
  322. var pageStart = (currentPage - 1) * pageSize;
  323. sql += string.Format(" WHERE Row > {0} AND Row <={1}", pageStart, (pageStart + pageSize));
  324. countSQL += where;
  325. result.TotalRecords = Scalar(countSQL, args);
  326. result.TotalPages = result.TotalRecords / pageSize;
  327. if (result.TotalRecords % pageSize > 0)
  328. result.TotalPages += 1;
  329. result.Items = Query(string.Format(sql, columns, TableName), args);
  330. return result;
  331. }
  332. /// <summary>
  333. /// Returns a single row from the database
  334. /// </summary>
  335. public virtual dynamic Single(string where, params object[] args) {
  336. var sql = string.Format("SELECT * FROM {0} WHERE {1}", TableName, where);
  337. return Query(sql, args).FirstOrDefault();
  338. }
  339. /// <summary>
  340. /// Returns a single row from the database
  341. /// </summary>
  342. public virtual dynamic Single(object key, string columns = "*") {
  343. var sql = string.Format("SELECT {0} FROM {1} WHERE {2} = @0", columns, TableName, PrimaryKeyField);
  344. return Query(sql, key).FirstOrDefault();
  345. }
  346. /// <summary>
  347. /// This will return a string/object dictionary for dropdowns etc
  348. /// </summary>
  349. public virtual IDictionary<string, object> KeyValues(string orderBy = "") {
  350. if (String.IsNullOrEmpty(DescriptorField))
  351. throw new InvalidOperationException("There's no DescriptorField set - do this in your constructor to describe the text value you want to see");
  352. var sql = string.Format("SELECT {0},{1} FROM {2} ", PrimaryKeyField, DescriptorField, TableName);
  353. if (!String.IsNullOrEmpty(orderBy))
  354. sql += "ORDER BY " + orderBy;
  355. return (IDictionary<string, object>)Query(sql);
  356. }
  357. /// <summary>
  358. /// This will return an Expando as a Dictionary
  359. /// </summary>
  360. public virtual IDictionary<string, object> ItemAsDictionary(ExpandoObject item) {
  361. return (IDictionary<string, object>)item;
  362. }
  363. //Checks to see if a key is present based on the passed-in value
  364. public virtual bool ItemContainsKey(string key, ExpandoObject item) {
  365. var dc = ItemAsDictionary(item);
  366. return dc.ContainsKey(key);
  367. }
  368. /// <summary>
  369. /// Executes a set of objects as Insert or Update commands based on their property settings, within a transaction.
  370. /// These objects can be POCOs, Anonymous, NameValueCollections, or Expandos. Objects
  371. /// With a PK property (whatever PrimaryKeyField is set to) will be created at UPDATEs
  372. /// </summary>
  373. public virtual int Save(params object[] things) {
  374. foreach (var item in things) {
  375. if (!IsValid(item)) {
  376. throw new InvalidOperationException("Can't save this item: " + String.Join("; ", Errors.ToArray()));
  377. }
  378. }
  379. var commands = BuildCommands(things);
  380. return Execute(commands);
  381. }
  382. public virtual DbCommand CreateInsertCommand(dynamic expando) {
  383. DbCommand result = null;
  384. var settings = (IDictionary<string, object>)expando;
  385. var sbKeys = new StringBuilder();
  386. var sbVals = new StringBuilder();
  387. var stub = "INSERT INTO {0} ({1}) \r\n VALUES ({2})";
  388. result = CreateCommand(stub, null);
  389. int counter = 0;
  390. foreach (var item in settings) {
  391. sbKeys.AppendFormat("{0},", item.Key);
  392. sbVals.AppendFormat("@{0},", counter.ToString());
  393. result.AddParam(item.Value);
  394. counter++;
  395. }
  396. if (counter > 0) {
  397. var keys = sbKeys.ToString().Substring(0, sbKeys.Length - 1);
  398. var vals = sbVals.ToString().Substring(0, sbVals.Length - 1);
  399. var sql = string.Format(stub, TableName, keys, vals);
  400. result.CommandText = sql;
  401. } else throw new InvalidOperationException("Can't parse this object to the database - there are no properties set");
  402. return result;
  403. }
  404. /// <summary>
  405. /// Creates a command for use with transactions - internal stuff mostly, but here for you to play with
  406. /// </summary>
  407. public virtual DbCommand CreateUpdateCommand(dynamic expando, object key) {
  408. var settings = (IDictionary<string, object>)expando;
  409. var sbKeys = new StringBuilder();
  410. var stub = "UPDATE {0} SET {1} WHERE {2} = @{3}";
  411. var args = new List<object>();
  412. var result = CreateCommand(stub, null);
  413. int counter = 0;
  414. foreach (var item in settings) {
  415. var val = item.Value;
  416. if (!item.Key.Equals(PrimaryKeyField, StringComparison.OrdinalIgnoreCase) && item.Value != null) {
  417. result.AddParam(val);
  418. sbKeys.AppendFormat("{0} = @{1}, \r\n", item.Key, counter.ToString());
  419. counter++;
  420. }
  421. }
  422. if (counter > 0) {
  423. //add the key
  424. result.AddParam(key);
  425. //strip the last commas
  426. var keys = sbKeys.ToString().Substring(0, sbKeys.Length - 4);
  427. result.CommandText = string.Format(stub, TableName, keys, PrimaryKeyField, counter);
  428. } else throw new InvalidOperationException("No parsable object was sent in - could not divine any name/value pairs");
  429. return result;
  430. }
  431. /// <summary>
  432. /// Removes one or more records from the DB according to the passed-in WHERE
  433. /// </summary>
  434. public virtual DbCommand CreateDeleteCommand(string where = "", object key = null, params object[] args) {
  435. var sql = string.Format("DELETE FROM {0} ", TableName);
  436. if (key != null) {
  437. sql += string.Format("WHERE {0}=@0", PrimaryKeyField);
  438. args = new object[] { key };
  439. } else if (!string.IsNullOrEmpty(where)) {
  440. sql += where.Trim().StartsWith("where", StringComparison.OrdinalIgnoreCase) ? where : "WHERE " + where;
  441. }
  442. return CreateCommand(sql, null, args);
  443. }
  444. public bool IsValid(dynamic item) {
  445. Errors.Clear();
  446. Validate(item);
  447. return Errors.Count == 0;
  448. }
  449. //Temporary holder for error messages
  450. public IList<string> Errors = new List<string>();
  451. /// <summary>
  452. /// Adds a record to the database. You can pass in an Anonymous object, an ExpandoObject,
  453. /// A regular old POCO, or a NameValueColletion from a Request.Form or Request.QueryString
  454. /// </summary>
  455. public virtual dynamic Insert(object o) {
  456. var ex = o.ToExpando();
  457. if (!IsValid(ex)) {
  458. throw new InvalidOperationException("Can't insert: " + String.Join("; ", Errors.ToArray()));
  459. }
  460. if (BeforeSave(ex)) {
  461. using (dynamic conn = OpenConnection()) {
  462. var cmd = CreateInsertCommand(ex);
  463. cmd.Connection = conn;
  464. cmd.ExecuteNonQuery();
  465. cmd.CommandText = "SELECT @@IDENTITY as newID";
  466. ex.ID = cmd.ExecuteScalar();
  467. Inserted(ex);
  468. }
  469. return ex;
  470. } else {
  471. return null;
  472. }
  473. }
  474. /// <summary>
  475. /// Updates a record in the database. You can pass in an Anonymous object, an ExpandoObject,
  476. /// A regular old POCO, or a NameValueCollection from a Request.Form or Request.QueryString
  477. /// </summary>
  478. public virtual int Update(object o, object key) {
  479. var ex = o.ToExpando();
  480. if (!IsValid(ex)) {
  481. throw new InvalidOperationException("Can't Update: " + String.Join("; ", Errors.ToArray()));
  482. }
  483. var result = 0;
  484. if (BeforeSave(ex)) {
  485. result = Execute(CreateUpdateCommand(ex, key));
  486. Updated(ex);
  487. }
  488. return result;
  489. }
  490. /// <summary>
  491. /// Removes one or more records from the DB according to the passed-in WHERE
  492. /// </summary>
  493. public int Delete(object key = null, string where = "", params object[] args) {
  494. var deleted = this.Single(key);
  495. var result = 0;
  496. if (BeforeDelete(deleted)) {
  497. result = Execute(CreateDeleteCommand(where: where, key: key, args: args));
  498. Deleted(deleted);
  499. }
  500. return result;
  501. }
  502. public void DefaultTo(string key, object value, dynamic item) {
  503. if (!ItemContainsKey(key, item)) {
  504. var dc = (IDictionary<string, object>)item;
  505. dc[key] = value;
  506. }
  507. }
  508. //Hooks
  509. public virtual void Validate(dynamic item) { }
  510. public virtual void Inserted(dynamic item) { }
  511. public virtual void Updated(dynamic item) { }
  512. public virtual void Deleted(dynamic item) { }
  513. public virtual bool BeforeDelete(dynamic item) { return true; }
  514. public virtual bool BeforeSave(dynamic item) { return true; }
  515. //validation methods
  516. public virtual void ValidatesPresenceOf(object value, string message = "Required") {
  517. if (value == null)
  518. Errors.Add(message);
  519. if (String.IsNullOrEmpty(value.ToString()))
  520. Errors.Add(message);
  521. }
  522. //fun methods
  523. public virtual void ValidatesNumericalityOf(object value, string message = "Should be a number") {
  524. var type = value.GetType().Name;
  525. var numerics = new string[] { "Int32", "Int16", "Int64", "Decimal", "Double", "Single", "Float" };
  526. if (!numerics.Contains(type)) {
  527. Errors.Add(message);
  528. }
  529. }
  530. public virtual void ValidateIsCurrency(object value, string message = "Should be money") {
  531. if (value == null)
  532. Errors.Add(message);
  533. decimal val = decimal.MinValue;
  534. decimal.TryParse(value.ToString(), out val);
  535. if (val == decimal.MinValue)
  536. Errors.Add(message);
  537. }
  538. public int Count() {
  539. return Count(TableName);
  540. }
  541. public int Count(string tableName, string where="") {
  542. return (int)Scalar("SELECT COUNT(*) FROM " + tableName);
  543. }
  544. /// <summary>
  545. /// A helpful query tool
  546. /// </summary>
  547. public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) {
  548. //parse the method
  549. var constraints = new List<string>();
  550. var counter = 0;
  551. var info = binder.CallInfo;
  552. // accepting named args only... SKEET!
  553. if (info.ArgumentNames.Count != args.Length) {
  554. throw new InvalidOperationException("Please use named arguments for this type of query - the column name, orderby, columns, etc");
  555. }
  556. //first should be "FindBy, Last, Single, First"
  557. var op = binder.Name;
  558. var columns = " * ";
  559. string orderBy = string.Format(" ORDER BY {0}", PrimaryKeyField);
  560. string sql = "";
  561. string where = "";
  562. var whereArgs = new List<object>();
  563. //loop the named args - see if we have order, columns and constraints
  564. if (info.ArgumentNames.Count > 0) {
  565. for (int i = 0; i < args.Length; i++) {
  566. var name = info.ArgumentNames[i].ToLower();
  567. switch (name) {
  568. case "orderby":
  569. orderBy = " ORDER BY " + args[i];
  570. break;
  571. case "columns":
  572. columns = args[i].ToString();
  573. break;
  574. default:
  575. constraints.Add(string.Format(" {0} = @{1}", name, counter));
  576. whereArgs.Add(args[i]);
  577. counter++;
  578. break;
  579. }
  580. }
  581. }
  582. //Build the WHERE bits
  583. if (constraints.Count > 0) {
  584. where = " WHERE " + string.Join(" AND ", constraints.ToArray());
  585. }
  586. //probably a bit much here but... yeah this whole thing needs to be refactored...
  587. if (op.ToLower() == "count") {
  588. result = Scalar("SELECT COUNT(*) FROM " + TableName + where, whereArgs.ToArray());
  589. } else if (op.ToLower() == "sum") {
  590. result = Scalar("SELECT SUM(" + columns + ") FROM " + TableName + where, whereArgs.ToArray());
  591. } else if (op.ToLower() == "max") {
  592. result = Scalar("SELECT MAX(" + columns + ") FROM " + TableName + where, whereArgs.ToArray());
  593. } else if (op.ToLower() == "min") {
  594. result = Scalar("SELECT MIN(" + columns + ") FROM " + TableName + where, whereArgs.ToArray());
  595. } else if (op.ToLower() == "avg") {
  596. result = Scalar("SELECT AVG(" + columns + ") FROM " + TableName + where, whereArgs.ToArray());
  597. } else {
  598. //build the SQL
  599. sql = "SELECT TOP 1 " + columns + " FROM " + TableName + where;
  600. var justOne = op.StartsWith("First") || op.StartsWith("Last") || op.StartsWith("Get") || op.StartsWith("Single");
  601. //Be sure to sort by DESC on the PK (PK Sort is the default)
  602. if (op.StartsWith("Last")) {
  603. orderBy = orderBy + " DESC ";
  604. } else {
  605. //default to multiple
  606. sql = "SELECT " + columns + " FROM " + TableName + where;
  607. }
  608. if (justOne) {
  609. //return a single record
  610. result = Query(sql + orderBy, whereArgs.ToArray()).FirstOrDefault();
  611. } else {
  612. //return lots
  613. result = Query(sql + orderBy, whereArgs.ToArray());
  614. }
  615. }
  616. return true;
  617. }
  618. }
  619. }