andrew4582

Massive.Oracle DB

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