C# ORM 泛型與反射的交叉應用 - 更新表中除了主鍵的所有數據

老規矩,不多嗶嗶

步驟

  1. 獲取主鍵字段名稱
  2. 這裏就是與前面的博文基本一致了,獲取類型,通過類型獲取屬性,通過屬性匹配字段,不過這裏多了一次判斷,判斷匹配的字段是否是主鍵
    然後就愉快的拼接 SQL 字符串然後執行吧

畫重點

由於本人懶,所以前面一直不想多說 ORM 映射的問題,但是躲得了初一躲不過十五,這裏就是十五了(不好意思,一不小心就多了幾句廢話)。

我們是通過 反射機制來實現的 ORM 這裏就不得不提到 Attribute 類,我們所有想要實現映射的類都必須直接或者間接的繼承自此類纔可以(這裏說的比較絕對,具體我也沒有怎麼仔細查閱資料,有問題歡迎提出)

比如我們想要定義一個 表 的特性類的話,就可以這樣做

[AttributeUsage(AttributeTargets.Class,AllowMultiple =false,Inherited =false)]
    class TableAttribute : Attribute
    {
        private string _TableName;
        private string _PK;
        public TableAttribute(string tableName, string pk)
        {
            TableName = tableName;
            PK = pk;
        }
        public string TableName { get => _TableName; set => _TableName = value; }
        public string PK { get => _PK; set => _PK = value; }
    }

代碼是這樣,那麼裏面那些個我們以前基本沒有見過的屬性是什麼東西呢。

AttributeUsageAttribute

自定義屬性必不可少的一個東西,自定義屬性都是從它開始,我們通過它定義了這個特性類的一些主要屬性,比如這個類的應用場景,是否可以被繼承之類的。

對於它的衆多成員我這裏就只說我們常用的幾個,畢竟我懶。。

AttributeTargets

這個表示我們這個特性類可以被使用到什麼場景,具體它提供了一些選項

  1. AttributeTargets.All
    這個屬性可以被應用到程序中的所有元素上
  2. AttributeTargets.Class
    這個屬性只可以被用到 class 上
  3. AttributeTargets.Method
    這個屬性只可以被用到 Method 上
  4. AttributeTargets.Property
    這個屬性只可以被用到 Property 上
  5. 以此類推,它裏面的可選項還很多,我就不一一列舉了。

Inherited(繼承屬性)

這個屬性表示了我們的這個特性類是否允許被其他的特性類繼承,就相當於類被定義成 final 的類不可以被其他類繼承是一個意思,可選值是一對布爾值 true OR false,ture 代表可以被繼承,false 則不可以。

AllowMultiple(多屬性實例)

這個屬性表示是否允許元素中存在多個實例,同樣是一對布爾值,true 代表可以,false 代表不可以。這裏的多個實例的意思就是在同一個位置是否允許多次使用,假如我們 TestAttr 的是此屬性 true ,如下代碼就是行的,但是如果屬性是 false,編譯就會報錯

[TestAttr(...)]
[TestAttr(...)]

特別情況

這是在看別人博文的時候看到的,轉自此地

如果 AllowMultiple 屬性和 Inherited 屬性都設置爲 true,則從另一個類繼承的類可以繼承一個屬性,並在同一子類中應用同一屬性的另一個實例。如果 AllowMultiple 設置爲 false,則父類中的所有屬性值將被子類中同一屬性的新實例重寫。

本篇正題

獲取主鍵字段名稱解決方案

  1. 設置對象類主鍵字段名稱
    定義 表 的特性類的時候,把 PK 也加入他的特性中,在對象類定義時設置 PK
    或者有人覺得這樣不是很好,想要主鍵管理主鍵,也可以,另外定義一個主鍵特性類, 在對象類定義時設置 PK
  2. 在方法中通過 type.GetCustomAttributes(type,inherit) 方法獲取主鍵名稱

代碼

特性表中就定義了 PK

[AttributeUsage(AttributeTargets.Class,AllowMultiple =false,Inherited =false)]
    class TableAttribute : Attribute
    {
        private string _TableName;
        private string _PK;
        public TableAttribute(string tableName, string pk)
        {
            TableName = tableName;
            PK = pk;
        }
        public string TableName { get => _TableName; set => _TableName = value; }
        public string PK { get => _PK; set => _PK = value; }
    }

另外定義一個 PK 特性類來管理 PK

[AttributeUsage(AttributeTargets.Class,AllowMultiple =false,Inherited =false)]
    public class PrimaryKeyAttribute : Attribute
    {
        public PrimaryKeyAttribute(String primaryKey)
        {
            this.Value = primaryKey;
        }
        public string Value { get; protected set; }
        public bool autoIncrement = false;
    }

字段的特性類

[AttributeUsage(AttributeTargets.Property,AllowMultiple =false,Inherited =false)]
    public class FieldAttribute : Attribute
    {

        private string _Fields;
        private string _DataType;
        private int _ValueLength;
        public FieldAttribute(string fields, string types, int length)
        {
            Fields = fields;
            DataType = types;
            ValueLength = length;
        }
        public string Fields { get => _Fields; set => _Fields = value; }
        public string DataType { get => _DataType; set => _DataType = value; }
        public int ValueLength { get => _ValueLength; set => _ValueLength = value; }
    }

使用上面的特性類的 對象類

[Table("dbo.Alvin","ID")]
    [PrimaryKey("ID",autoIncrement = true)]
    public class Record
    {
        private int _ID;
        private string _Name;
        private DateTime _InDate;
        private string _InUser;
        private DateTime _LastEditDate;
        private string _LastEditUser;
        //必須要通過這種方式(公開)創建字段,否者會訪問不了
        [Field("Name", "nvarchar", 50)]
        public string Name { get => _Name; set => _Name = value; }
        [Field("InDate", "datetime", 0)]
        public DateTime InDate { get => _InDate; set => _InDate = value; }
        [Field("InUser", "varchar", 15)]
        public string InUser { get => _InUser; set => _InUser = value; }
        [Field("LastEditDate", "datetime", 0)]
        public DateTime LastEditDate { get => _LastEditDate; set => _LastEditDate = value; }
        [Field("LastEditUser", "varchar", 15)]
        public string LastEditUser { get => _LastEditUser; set => _LastEditUser = value; }
        [Field("ID","int",0)]
        public int ID { get => _ID; set => _ID = value; }
    }

輔助類(SqlHelper)

public class SqlHelper : ISqlHelper
    {
        public const string ConnectionStringCfgKey = "ConnectionString";
        private SqlHelper(){}
        private static readonly Lazy<SqlHelper> LazyInstance = new Lazy<SqlHelper>(
            ()=>new SqlHelper(), LazyThreadSafetyMode.ExecutionAndPublication);
        static SqlHelper()
        {

        }
        private static ISqlHelper instance = null;
        public static void SetInstance(ISqlHelper fakeSqlHelper)
        {
            instance = fakeSqlHelper;
        }
        public static void ResetInstance()
        {
            instance = null;
        }
        public static ISqlHelper Instance
        {
            get
            {
                if (null != instance) return instance;
                return LazyInstance.Value;
            }
        }
        public SqlConnection GetConnection()
        {
            return GetConnection(null);
        }
        public SqlConnection GetConnection(string connectionString)
        {
            if (string.IsNullOrWhiteSpace(connectionString))
            {
                //connectionString = 
                //    ConfigurationManager.AppSettings.Get(ConnectionStringCfgKey);
                connectionString = @"Server=SCMISBIZTALK01;UID=intern;Password=Th!sls@l0ngPWD;Database=CSharpTraining";
            }
            if (string.IsNullOrWhiteSpace(connectionString))
            {
                throw new ArgumentException(
                    "Connection string must be specified " +
                    "either from parameter or App.config file.");
            }
            return new SqlConnection(connectionString);
        }
        private void EnsureConnectionOpened(DbConnection conn)
        {
            if (conn.State == ConnectionState.Open) return;
            conn.Open();
        }
        private SqlCommand BuildCommnand(SqlConnection conn,
            string sql,
            List<SqlParameter> parameters)
        {
            var cmd = new SqlCommand(sql, conn);
            if (null != parameters)
            {
                parameters.ForEach(p => cmd.Parameters.Add(p));
            }
            return cmd;
        }
        public SqlDataReader ExecuteQuery(SqlConnection conn, 
            string sql, 
            List<SqlParameter> parameters)
        {
            var cmd = BuildCommnand(conn, sql, parameters);
            EnsureConnectionOpened(conn);
           return cmd.ExecuteReader();
        }
        public int ExecuteNonQuery(string sql, List<SqlParameter> parameters)
        {
            using (var conn = GetConnection())
            {
                return ExecuteNonQuery(conn, sql, parameters);
            }
        }
        public int ExecuteNonQuery(SqlConnection conn,
            string sql,
            List<SqlParameter> parameters)
        {
            var cmd = BuildCommnand(conn, sql, parameters);
            EnsureConnectionOpened(conn);
            return cmd.ExecuteNonQuery();
        }
        public T ExecuteScalar<T>(SqlConnection conn,
            string sql,
            List<SqlParameter> parameters) 
        {
            var cmd = BuildCommnand(conn, sql, parameters);
            EnsureConnectionOpened(conn);
            var result = cmd.ExecuteScalar();
            if (null == result || DBNull.Value == result)
            {
                return default(T);
            }
            return (T)Convert.ChangeType(result, typeof (T));
        }
        public T ExecuteScalar<T>(string sql, List<SqlParameter> parameters)
        {
            using (var conn = GetConnection())
            {
                return ExecuteScalar<T>(conn, sql, parameters);
            }
        }
    }

操作方法(代碼中應有的解釋我已經註釋出來)

private const string UPDATE = "UPDATE dbo.Alvin SET ";

public static int Update<TEntity>(TEntity entity)
        {
            using (var conn = SqlHelper.Instance.GetConnection())
            {
                Type type = typeof(TEntity);
                PropertyInfo[] infos = type.GetProperties();
                //獲取主鍵的字段
                string PK = "";
                //兩種方式
                //方式一
                /*object[] pkOBJ = type.GetCustomAttributes(typeof(TableAttribute),false);
                if (pkOBJ != null)
                {
                    PK = ((TableAttribute)pkOBJ[0]).PK;
                    Console.WriteLine("字段名稱:{0}", PK);
                }*/
                //方式二
                object[] pkOBJ = type.GetCustomAttributes(typeof(PrimaryKeyAttribute), false);
                if (pkOBJ != null)
                {
                    PK = ((PrimaryKeyAttribute)pkOBJ[0]).Value;
                    Console.WriteLine("字段名稱:{0}", PK);
                }
                //定義SB
                StringBuilder sb = new StringBuilder();
                foreach (var info in infos)
                {
                    object[] objs = info.GetCustomAttributes(typeof(FieldAttribute),false);
                    foreach (var obj in objs)
                    {
                        string key = ((FieldAttribute)obj).Fields;
                        //判斷是否爲主鍵,是就跳過
                        if (!key.Equals(PK))
                        {
                            //不是主鍵,則可以繼續操作
                            sb.Append(key + "=");
                            sb.Append("'"+type.GetProperty(key).GetValue(entity, null)+"',");
                        }
                    }
                }
                //去掉多餘的 , 號
                sb.Append(")");
                sb.Replace(",)", "");
                //拼接 執行 sql
                string sql = UPDATE + sb.ToString();
                int updateNum = SqlHelper.Instance.ExecuteNonQuery(conn,
                    sql,
                    new List<SqlParameter>() { });
                return updateNum;
            }
        }

效果

結果

數據庫

數據庫

發佈了62 篇原創文章 · 獲贊 35 · 訪問量 19萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章