• ObjectExtensions


    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Dynamic;
    using System.Text.RegularExpressions;
    using System.Globalization;
    using System.Collections.Specialized;
    using System.Data;
    using System.Configuration;
    using System.Data.Common;
    
    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>
            public static void AddParam(this DbCommand cmd, object item) {
                var p = cmd.CreateParameter();
                p.ParameterName = string.Format("@{0}", cmd.Parameters.Count);
    
                //fix for NULLs as parameter values
                if (item == null) {
                    p.Value = DBNull.Value;
                } else {
                    //fix for Guids
                    if (item.GetType() == typeof(Guid)) {
                        p.Value = item.ToString();
                        p.DbType = DbType.String;
                    } else {
                        p.Value = item;
                    }
                }
                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>();
    
                //work with the Expando as a Dictionary
                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>
            /// <param name="o"></param>
            /// <returns></returns>
            public static dynamic ToExpando(this object o) {
                var result = new ExpandoObject();
    
                //work with the Expando as a Dictionary
                var d = result as IDictionary<string, object>;
                //shouldn't have to... but just in case
                if (o.GetType() == typeof(ExpandoObject)) {
                    return o;
                }
                
                //special for form submissions
                if (o.GetType() == typeof(NameValueCollection)) {
                    var nv = (NameValueCollection)o;
                    //there's a better way to do this... I just don't know what it is...
                    foreach (var item in nv.Keys) {
                        var key = item.ToString();
                        d.Add(key, nv.Get(key));
                    }
    
                } else {
                    //assume it's a regular lovely object
                    var props = o.GetType().GetProperties();
                    foreach (var item in props) {
                        var val = item.GetValue(o, null);
                        d.Add(item.Name, val);
                    }
                }
                return result;
            }
    
            /// <summary>
            /// Turns the object into a Dictionary
            /// </summary>
            /// <param name="thingy"></param>
            /// <returns></returns>
            public static IDictionary<string, object> ToDictionary(this object thingy) {
                var expando = thingy.ToExpando();
                return (IDictionary<string, object>)expando;
            }   
        }
    
        /// <summary>
        /// A class that wraps your database table in Dynamic Funtime
        /// </summary>
        public abstract class DynamicModel:DynamicObject {
            DbProviderFactory _factory;
            string _connectionStringName;
            string _connectionString;
    
            public IList<dynamic> Query(string sql, params object[] args) {
                var result = new List<dynamic>();
                var cmd = CreateCommand(sql, args);
                using (var conn = OpenConnection()) {
                    cmd.Connection= conn;
                    var rdr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
                    result = rdr.ToExpandoList();
    
                    //can't help it - like being explicit
                    rdr.Dispose();
                    cmd.Dispose();
                }
                return result;
            }
    
            /// <summary>
            /// Creates a DBCommand that you can use for loving your database.
            /// </summary>
            DbCommand CreateCommand(string sql, params object[] args) {
                DbCommand result = null;
                result = _factory.CreateCommand();
                result.CommandText = sql;
                if(args.Length > 0)
                    result.AddParams(args);
                return result;
            }
            DbConnection GetConnection() {
                var connection = _factory.CreateConnection();
                connection.ConnectionString = _connectionString;
                return connection;
            }
            DbConnection OpenConnection() {
                var conn = GetConnection();
                conn.Open();
                return conn;
            }
            /// <summary>
            /// Creates a slick, groovy little wrapper for your action
            /// </summary>
            /// <param name="connectionStringName"></param>
            public DynamicModel(string connectionStringName) {
                //can be overridden by property setting
                TableName = this.GetType().Name;
                _connectionStringName = connectionStringName;
    
                var providerName = "System.Data.SqlClient";
                if (ConfigurationManager.ConnectionStrings[_connectionStringName] != null) {
                    providerName = ConfigurationManager.ConnectionStrings[_connectionStringName].ProviderName ?? "System.Data.SqlClient";
                } else {
                    throw new InvalidOperationException("Can't find a connection string with the name '" + _connectionStringName + "'");
                }
                _factory = DbProviderFactories.GetFactory(providerName);
                _connectionString = ConfigurationManager.ConnectionStrings[_connectionStringName].ConnectionString;
            }
    
            string _primaryKeyField;
            /// <summary>
            /// Conventionally returns a PK field. The default is "ID" if you don't set one
            /// </summary>
            public string PrimaryKeyField {
                get {
                    //a bit of convention here
                    if (!string.IsNullOrEmpty(_primaryKeyField))
                        return _primaryKeyField;
                    
                    //oh well - did our best
                    return "ID";
                }
                set {
                    _primaryKeyField = value;
                }
            }
    
            /// <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) {
                var result = o.ToDictionary().ContainsKey(PrimaryKeyField);
                return result;
            }
            
            /// <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) {
                var d = o.ToDictionary();
                object result = null;
                d.TryGetValue(PrimaryKeyField, out result);
                return result;
            }
            
            /// <summary>
            /// The name of the Database table we're working with. This defaults to 
            /// the class name - set this value if it's different
            /// </summary>
            public string TableName { get; set; }
    
            /// <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 dynamic Insert(object o) {
                dynamic result = 0;
                
                if (BeforeInsert(o)) {
                    var cmd = CreateInsertCommand(o);
                    using (var conn = OpenConnection()) {
                        cmd.Connection = conn;
                        result = cmd.ExecuteScalar();
                        cmd.Dispose();
                        AfterInsert(o);
                    }
                }
                return result;
            }
    
            /// <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;
    
                //turn this into an expando - we'll need that for the validators
                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}); \r\nSELECT SCOPE_IDENTITY()";
                result = CreateCommand(stub);
    
                int counter = 0;
                foreach (var item in settings) {
                    sbKeys.AppendFormat("{0},", item.Key);
                    sbVals.AppendFormat("@{0},", counter.ToString());
                    result.AddParam(item.Value);
                    counter++;
    
                }
                if (counter > 0) {
                    //strip off trailing commas
                    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;
            }
    
            /// <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);
                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++;
                    }
                }
                if (counter > 0) {
                    //add the key
                    result.AddParam(key);
                    //strip the last commas
                    var keys = sbKeys.ToString().Substring(0, sbKeys.Length - 4);
                    var sql = string.Format(stub, TableName, keys, PrimaryKeyField, counter);
    
                    result.CommandText = sql;
                } else {
                    //throw
                    throw new InvalidOperationException("No parsable object was sent in - could not divine any name/value pairs");
                }
                return result;
            }
            /// <summary>
            /// Updates a record in 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 int Update(object o, object key) {
                //turn this into an expando - we'll need that for the validators
                int result = 0;
                if (BeforeUpdate(o)) {
                    using (var conn = OpenConnection()) {
                        var cmd = CreateUpdateCommand(o, key);
                        result = cmd.ExecuteNonQuery();
                        AfterUpdate(o);
                    }
    
                }
                return result;
    
            }
            /// <summary>
            /// Updates a bunch of records in the database within a transaction. You can pass Anonymous objects, ExpandoObjects,
            /// Regular old POCOs - these all have to have a PK set
            /// </summary>
            public int InsertMany(IEnumerable<object> things) {
                int result = 0;
    
                using (var conn = OpenConnection()) {
                    var tx = conn.BeginTransaction();
                    foreach (var item in things) {
                        if (BeforeInsert(item)) {
                            var cmd = CreateInsertCommand(item);
                            cmd.Connection = conn;
                            cmd.Transaction = tx;
                            cmd.ExecuteNonQuery();
                            cmd.Dispose();
                            AfterInsert(item);
                        }
                        result++;
                    }
                    tx.Commit();
                    conn.Dispose();
                    tx.Dispose();
                }
    
                return result;
    
            }
            /// <summary>
            /// Updates a bunch of records in the database within a transaction. You can pass Anonymous objects, ExpandoObjects,
            /// Regular old POCOs - these all have to have a PK set
            /// </summary>
            public int UpdateMany(IEnumerable<object> things) {
                //turn this into an expando - we'll need that for the validators
                int result = 0;
                
                using (var conn = OpenConnection()) {
                    var tx = conn.BeginTransaction();
                    foreach (var item in things) {
                        var pk = GetPrimaryKey(item);
                        if (pk == null)
                            throw new InvalidOperationException("Please be sure to set a value for the primary key");
                        if (BeforeUpdate(item)) {
                            var cmd = CreateUpdateCommand(item, pk);
                            cmd.Connection = conn;
                            cmd.Transaction = tx;
                            cmd.ExecuteNonQuery();
                            cmd.Dispose();
                            AfterUpdate(item);
                            result++;
                        }
    
                    }
                    tx.Commit();
                    conn.Dispose();
                    tx.Dispose();
                }
    
                return result;
    
            }
            /// <summary>
            /// If you're feeling lazy, or are just unsure about whether to use Update or Insert you can use
            /// this method. It will look for a PrimaryKeyField with a set value to determine if this should
            /// be an Insert or Save. 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 dynamic Save(object o) {
                dynamic result = 0;
                if (BeforeSave(o)) {
                    var expando = o.ToExpando();
                    //decide insert or update
                    if (HasPrimaryKey(expando)) {
                        result = Update(expando, GetPrimaryKey(o));
    
                    } else {
                        result = Insert(expando);
                    }
                    AfterSave(o);
                }
                return result;
            }    
    
            /// <summary>
            /// Removes a record from the database
            /// </summary>
            public int Delete(object key) {
                //execute
                var sql = string.Format("DELETE FROM {0} WHERE {1} = @0", TableName, PrimaryKeyField);
                var result = 0;
                var cmd = CreateCommand(sql, key);
                using (var conn = OpenConnection()) {
                    cmd.Connection = conn;
                    result = cmd.ExecuteNonQuery();
                    cmd.Dispose();
                }
                return result;
            }
    
            /// <summary>
            /// Removes one or more records from the DB according to the passed-in WHERE
            /// </summary>
            public dynamic Delete(string where, params object[] args) {
                //execute
                var sql = string.Format("DELETE FROM {0} ", TableName);
                if (!where.Trim().StartsWith("where", StringComparison.CurrentCultureIgnoreCase)) {
                    sql += "WHERE ";
                }
                sql += where;
                var result = 0;
                var cmd = CreateCommand(sql, args);
                using (var conn = OpenConnection()) {
                    cmd.Connection = conn;
                    result = cmd.ExecuteNonQuery();
                    cmd.Dispose();
                }
                return result;
            }
    
            /// <summary>
            /// 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, params object[] args) {
                string sql = "SELECT * FROM {0} ";
                if (limit > 0) {
                    sql = "SELECT TOP "+limit+" * FROM {0} ";
                }
    
                if (!string.IsNullOrEmpty(where)) {
                    if (where.Trim().StartsWith("where", StringComparison.CurrentCultureIgnoreCase)) {
                        sql += where;
                    } else {
                        sql += "WHERE " + where;
                    }
                }
    
                if (!String.IsNullOrEmpty(orderBy)) {
                    if (!orderBy.Trim().StartsWith("order by", StringComparison.CurrentCultureIgnoreCase)) {
                        sql += " ORDER BY ";
                    }
                    sql += orderBy;
                }
                return Query(string.Format(sql, TableName), args);           
            }
    
            /// <summary>
            /// Returns a single row from the database
            /// </summary>
            /// <returns>ExpandoObject</returns>
            public dynamic Single(object key) {
                var sql = string.Format("SELECT * FROM {0} WHERE {1} = @0", TableName, PrimaryKeyField);
                var single = Query(sql,key).FirstOrDefault();
                return single;            
            }
            #region hooks
            //hooks for save routines
            public virtual bool BeforeInsert(object o) {
                return true;
            }
            public virtual bool BeforeUpdate(object o) {
                return true;
            }
            public virtual bool BeforeSave(object o) {
                return true;
            }
            public virtual bool BeforeDelete(object key) {
                return true;
            }
    
            public virtual void AfterInsert(object o) { }
            public virtual void AfterUpdate(object o) { }
            public virtual void AfterSave(object o) { }
            public virtual void AfterDelete(object key) { }
            #endregion
        }
    }
  • 相关阅读:
    SQL Server 2019 新版本
    SQL Server 中的窗口函数(2012 新函数)
    MySQL 学习(二)总体框架 & redo-log 和 bin-log 的介绍
    redis(四)集群(Sentinel)
    HahMap相关问题
    JVM工具使用和Linux-top命令解析
    缓存知识点
    消息队列(六)--- RocketMQ-消息消费
    redis(三)数据类型--位图相关
    分布式哈希一致性
  • 原文地址:https://www.cnblogs.com/zcm123/p/2999600.html
Copyright © 2020-2023  润新知