PageRenderTime 56ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Roster/Roster/Infrastucture/Massive/Massive.cs

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