主頁 > .NET開發 > C# ORM學習筆記:使用特性+反射實作簡單ORM

C# ORM學習筆記:使用特性+反射實作簡單ORM

2020-09-15 13:29:01 .NET開發

    一、原理與環境

    在生成資料表的物體類時,利用自定義特性,給它打上表及欄位的特性,然后使用反射原理,將自定義特性拼接成增、刪、改、查對應的SQL,即可完成一個簡單的ORM,

    本示例的執行環境:

    1)資料庫:SQL Server,(可根據自己的需要,建立不同的資料庫工廠,)

    2)資料表:需使用自增型別(identity)作為資料表的主鍵,主鍵名字可以隨便起,如ID,

    3)物體類:物體類需提供無參建構式,

    二、演示資料表

    Person表,包含主鍵(ID)、姓名(Name)、年齡(Age)、性別(Gender),

CREATE TABLE [dbo].[Person](
    [ID] [BIGINT] IDENTITY(1,1) NOT NULL,
    [Name] [NVARCHAR](50) NULL,
    [Age] [INT] NULL,
    [Gender] [NVARCHAR](10) NULL,
 CONSTRAINT [PK_Person] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

    二、自定義特性

    定義兩個自定義特性:

    2.1、DataTableAttribute

    此為資料表特性,包含表名(TableName)、主鍵(Key),

    [Serializable]
    public class DataTableAttribute : Attribute
    {
        /// <summary>
        /// 資料表
        /// </summary>
        public string TableName { get; }

        /// <summary>
        /// 主鍵
        /// </summary>
        public string Key { get; }

        /// <summary>
        /// 建構式
        /// </summary>
        /// <param name="tableName"></param>
        /// <param name="key"></param>
        public DataTableAttribute(string tableName, string key)
        {
            TableName = tableName;
            Key = key;
        }
    }

    2.2、DataFieldAttribute

    此為欄位特性,包含欄位名(FieldName)、欄位型別(FieldType)、長度(Length)、是否自增(IsIdentity),

    [Serializable]
    public class DataFieldAttribute : Attribute
    {
        public string FieldName { get; set; }
        public string FieldType { get; set; }
        public int Length { get; set; }
        public bool IsIdentity { get; set; }

        /// <summary>
        /// 建構式
        /// </summary>
        /// <param name="fieldName">欄位名</param>
        /// <param name="fieldType">欄位型別</param>
        /// <param name="length">長度</param>
        /// <param name="isIdentity">是否自增長</param>
        public DataFieldAttribute(string fieldName, string fieldType, int length, bool isIdentity)
        {
            FieldName = fieldName;
            FieldType = fieldType;
            Length = length;
            IsIdentity = isIdentity;
        }

        /// <summary>
        /// 建構式
        /// </summary>
        /// <param name="fieldName"></param>
        /// <param name="length"></param>
        /// <param name="isIdentity"></param>
        public DataFieldAttribute(string fieldName, string fieldType, int length) : this(fieldName, fieldType, length, false)
        { }

        /// <summary>
        /// 建構式
        /// </summary>
        /// <param name="fieldName"></param>
        /// <param name="fieldtype"></param>
        public DataFieldAttribute(string fieldName, string fieldType) : this(fieldName, fieldType, 0, false)
        { }

        /// <summary>
        /// 建構式
        /// </summary>
        /// <param name="fieldName"></param>
        /// <param name="isIdentity"></param>
        public DataFieldAttribute(string fieldName, bool isIdentity) : this(fieldName, "", 0, isIdentity)
        { }

        /// <summary>
        /// 建構式
        /// </summary>
        /// <param name="fieldName"></param>
        public DataFieldAttribute(string fieldName) : this(fieldName, false)
        { }
    }

    三、生成物體類

    3.1、物體類樣式

    依照前面的規劃,Person表需要生成下面這個樣子:

using System;
using System.Collections.Generic;
using System.Text;
using LinkTo.ORM.CustomAttribute;

namespace LinkTo.ORM.Model
{
    [DataTable("Person","ID")]
    [Serializable]
    public class Person
    {
        public Person() { }

        [DataField("ID","bigint",19,true)]
        public long? ID {get; set;}

        [DataField("Name","nvarchar",50,false)]
        public string Name {get; set;}

        [DataField("Age","int",10,false)]
        public int? Age {get; set;}

        [DataField("Gender","nvarchar",10,false)]
        public string Gender {get; set;}
    }
}

    3.2、使用T4模板生成物體類

    3.2.1、T4Code檔案夾的文本模板

<#@ assembly name="System.Core" #>
<#@ assembly name="System.Data" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Data"#>
<#@ import namespace="System.Data.SqlClient"#>
<#+
    #region T4Code
    /// <summary>
    /// 資料庫架構介面
    /// </summary>
    public interface IDBSchema : IDisposable
    {
        List<string> GetTableList();
        DataTable GetTableMetadata(string tableName);
    }

    /// <summary>
    /// 資料庫架構工廠
    /// </summary>
    public class DBSchemaFactory
    {
        static readonly string DatabaseType = "SqlServer";
        public static IDBSchema GetDBSchema()
        {
            IDBSchema dbSchema;
            switch (DatabaseType) 
            {
                case "SqlServer":
                    {
                        dbSchema =new SqlServerSchema();
                        break;
                    }
                default: 
                    {
                        throw new ArgumentException("The input argument of DatabaseType is invalid.");
                    }
            }
            return dbSchema;
        }
    }

    /// <summary>
    /// SqlServer
    /// </summary>
    public class SqlServerSchema : IDBSchema
    {
        public string ConnectionString = "Server=.;Database=Test;Uid=sa;Pwd=********;";
        public SqlConnection conn;

        public SqlServerSchema()
        {
            conn = new SqlConnection(ConnectionString);
            conn.Open();
        }

        public List<string> GetTableList()
        {
            List<string> list = new List<string>();
            string commandText = "SELECT NAME TABLE_NAME FROM SYSOBJECTS WHERE XTYPE='U' ORDER BY NAME";

            using(SqlCommand cmd = new SqlCommand(commandText, conn))
            {
                using (SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.CloseConnection))
                {
                    while (dr.Read())
                    {
                        list.Add(dr["TABLE_NAME"].ToString());
                    }
                }
            }

            return list;
        }
        
        public DataTable GetTableMetadata(string tableName)
        {
            string commandText=string.Format
                (
                    "SELECT A.NAME TABLE_NAME,B.NAME FIELD_NAME,C.NAME DATATYPE,ISNULL(B.PREC,0) LENGTH, "+
                        "CONVERT(BIT,CASE WHEN NOT F.ID IS NULL THEN 1 ELSE 0 END) ISKEY, "+
                        "CONVERT(BIT,CASE WHEN COLUMNPROPERTY(B.ID,B.NAME,'ISIDENTITY') = 1 THEN 1 ELSE 0 END) AS ISIDENTITY, "+
                        "CONVERT(BIT,B.ISNULLABLE) ISNULLABLE "+
                    "FROM SYSOBJECTS A INNER JOIN SYSCOLUMNS B ON A.ID=B.ID INNER JOIN SYSTYPES C ON B.XTYPE=C.XUSERTYPE "+
                        "LEFT JOIN SYSOBJECTS D ON B.ID=D.PARENT_OBJ AND D.XTYPE='PK' "+
                        "LEFT JOIN SYSINDEXES E ON B.ID=E.ID AND D.NAME=E.NAME "+
                        "LEFT JOIN SYSINDEXKEYS F ON B.ID=F.ID AND B.COLID=F.COLID AND E.INDID=F.INDID "+
                    "WHERE A.XTYPE='U' AND A.NAME='{0}' "+
                    "ORDER BY A.NAME,B.COLORDER", tableName
                );

            using(SqlCommand cmd = new SqlCommand(commandText, conn))
            {
                SqlDataAdapter da = new SqlDataAdapter(cmd);
                DataSet ds = new DataSet();
                da.Fill(ds,"Schema");
                return ds.Tables[0];
            }
        }

        public void Dispose()
        {
            if (conn != null)
            {
                conn.Close();
            }
        }
    }
    #endregion
#>
DBSchema.ttinclude
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Data" #>
<#@ assembly name="EnvDTE" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Data"#>
<#@ import namespace="System.IO"#>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating"#>

<#+
// T4 Template Block manager for handling multiple file outputs more easily.
// Copyright (c) Microsoft Corporation.All rights reserved.
// This source code is made available under the terms of the Microsoft Public License (MS-PL)

// Manager class records the various blocks so it can split them up
class Manager
{
    public struct Block
    {
        public string Name;
        public int Start, Length;
    }

    public List<Block> blocks = new List<Block>();
    public Block currentBlock;
    public Block footerBlock = new Block();
    public Block headerBlock = new Block();
    public ITextTemplatingEngineHost host;
    public ManagementStrategy strategy;
    public StringBuilder template;
    public string OutputPath { get; set; }

    public Manager(ITextTemplatingEngineHost host, StringBuilder template, bool commonHeader)
    {
        this.host = host;
        this.template = template;
        OutputPath = string.Empty;
        strategy = ManagementStrategy.Create(host);
    }

    public void StartBlock(string name)
    {
        currentBlock = new Block { Name = name, Start = template.Length };
    }

    public void StartFooter()
    {
        footerBlock.Start = template.Length;
    }

    public void EndFooter()
    {
        footerBlock.Length = template.Length - footerBlock.Start;
    }

    public void StartHeader()
    {
        headerBlock.Start = template.Length;
    }

    public void EndHeader()
    {
        headerBlock.Length = template.Length - headerBlock.Start;
    }    

    public void EndBlock()
    {
        currentBlock.Length = template.Length - currentBlock.Start;
        blocks.Add(currentBlock);
    }

    public void Process(bool split)
    {
        string header = template.ToString(headerBlock.Start, headerBlock.Length);
        string footer = template.ToString(footerBlock.Start, footerBlock.Length);
        blocks.Reverse();
        foreach(Block block in blocks) {
            string fileName = Path.Combine(OutputPath, block.Name);
            if (split) {
                string content = header + template.ToString(block.Start, block.Length) + footer;
                strategy.CreateFile(fileName, content);
                template.Remove(block.Start, block.Length);
            } else {
                strategy.DeleteFile(fileName);
            }
        }
    }
}

class ManagementStrategy
{
    internal static ManagementStrategy Create(ITextTemplatingEngineHost host)
    {
        return (host is IServiceProvider) ? new VSManagementStrategy(host) : new ManagementStrategy(host);
    }

    internal ManagementStrategy(ITextTemplatingEngineHost host) { }

    internal virtual void CreateFile(string fileName, string content)
    {
        File.WriteAllText(fileName, content);
    }

    internal virtual void DeleteFile(string fileName)
    {
        if (File.Exists(fileName))
            File.Delete(fileName);
    }
}

class VSManagementStrategy : ManagementStrategy
{
    private EnvDTE.ProjectItem templateProjectItem;

    internal VSManagementStrategy(ITextTemplatingEngineHost host) : base(host)
    {
        IServiceProvider hostServiceProvider = (IServiceProvider)host;
        if (hostServiceProvider == null)
            throw new ArgumentNullException("Could not obtain hostServiceProvider");

        EnvDTE.DTE dte = (EnvDTE.DTE)hostServiceProvider.GetService(typeof(EnvDTE.DTE));
        if (dte == null)
            throw new ArgumentNullException("Could not obtain DTE from host");

        templateProjectItem = dte.Solution.FindProjectItem(host.TemplateFile);
    }

    internal override void CreateFile(string fileName, string content)
    {
        base.CreateFile(fileName, content);
        ((EventHandler)delegate { templateProjectItem.ProjectItems.AddFromFile(fileName); }).BeginInvoke(null, null, null, null);
    }

    internal override void DeleteFile(string fileName)
    {
        ((EventHandler)delegate { FindAndDeleteFile(fileName); }).BeginInvoke(null, null, null, null);
    }

    private void FindAndDeleteFile(string fileName)
    {
        foreach(EnvDTE.ProjectItem projectItem in templateProjectItem.ProjectItems)
        {
            if (projectItem.get_FileNames(0) == fileName)
            {
                projectItem.Delete();
                return;
            }
        }
    }
}
#>
MultiDocument.ttinclude

    DBSchema.ttinclude主要實作了資料庫工廠的功能,注:請將資料庫連接字串改成您自己的,

    MultiDocument.ttinclude主要實作了多檔案的功能,

    3.2.2、生成物體類的文本模板

<#@ template debug="true" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
<#@ include file="T4Code/DBSchema.ttinclude"#>
<#@ include file="T4Code/MultiDocument.ttinclude"#>
<# var manager = new Manager(Host, GenerationEnvironment, true) { OutputPath = Path.GetDirectoryName(Host.TemplateFile)}; #>
<#
    //System.Diagnostics.Debugger.Launch();//除錯
    var dbSchema = DBSchemaFactory.GetDBSchema();
    List<string> tableList = dbSchema.GetTableList();
    foreach (string tableName in tableList)
    {
        manager.StartBlock(tableName+".cs");
        DataTable table = dbSchema.GetTableMetadata(tableName);

        //獲取主鍵
        string strKey = string.Empty;
        foreach (DataRow dataRow in table.Rows)
        {
            if ((bool)dataRow["ISKEY"] == true)
            {
                strKey = dataRow["FIELD_NAME"].ToString();
                break;
            }
        }
        
#>
//-------------------------------------------------------------------------------
// 此代碼由T4模板MultModelAuto自動生成
// 生成時間 <#= DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") #>
// 對此檔案的更改可能會導致不正確的行為,并且如果重新生成代碼,這些更改將會丟失,
//-------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Text;
using LinkTo.ORM.CustomAttribute;

namespace LinkTo.ORM.Model
{
    [DataTable("<#= tableName #>","<#= strKey #>")]
    [Serializable]
    public class <#= tableName #>
    {
        public <#= tableName #>() { }
<#
        foreach (DataRow dataRow in table.Rows)
        {
            //獲取資料型別
            string dbDataType = dataRow["DATATYPE"].ToString();
            string dataType = string.Empty;
                    
            switch (dbDataType)
            {
                case "decimal":
                case "numeric":
                case "money":
                case "smallmoney":
                    dataType = "decimal?";
                    break;
                case "char":
                case "nchar":
                case "varchar":
                case "nvarchar":
                case "text":
                case "ntext":
                    dataType = "string";
                    break;
                case "uniqueidentifier":
                    dataType = "Guid?";
                    break;
                case "bit":
                    dataType = "bool?";
                    break;
                case "real":
                    dataType = "Single?";
                    break;
                case "bigint":
                    dataType = "long?";
                    break;
                case "int":
                    dataType = "int?";
                    break;
                case "tinyint":
                case "smallint":
                    dataType = "short?";
                    break;
                case "float":
                    dataType = "float?";
                    break;
                case "date":
                case "datetime":
                case "datetime2":
                case "smalldatetime":
                    dataType = "DateTime?";
                    break;
                case "datetimeoffset ":
                    dataType = "DateTimeOffset?";
                    break;
                case "timeSpan ":
                    dataType = "TimeSpan?";
                    break;
                case "image":
                case "binary":
                case "varbinary":
                    dataType = "byte[]";
                    break;
                default:
                    break;
            }
#>

        [DataField("<#= dataRow["FIELD_NAME"].ToString() #>","<#= dataRow["DATATYPE"].ToString() #>",<#= dataRow["LENGTH"].ToString() #>,<#= dataRow["ISIDENTITY"].ToString().ToLower() #>)]
        public <#= dataType #> <#= dataRow["FIELD_NAME"].ToString() #> {get; set;}
<#
        }
#>
    }
}
<#
        manager.EndBlock();
    }
    dbSchema.Dispose();
    manager.Process(true);
#>
MultiModelAuto.tt

    注:由于ORM拼接SQL時使用的是表特性及欄位特性,可以看出表特性上使用的表名、欄位特性上使用的欄位名,都是與資料庫一致的,有了這個保障,資料表生成物體類的時候,類名是可以更改的,因為我只需要保證表特性與資料庫一致即可,舉個例子,我有個資料表Person_A,在生成物體類時,類名可以生成為Class PersonA {...},但是表特性依然是[DataTable("Person_A","...")],相同的原理,屬性名也是可以更改的,

    四、ORM實作

    資料表的CURD,主要是通過反射來實作SQL拼接,實作如下:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using LinkTo.ORM.CustomAttribute;

namespace LinkTo.ORM
{
    public static class DBHelper
    {
        public static readonly string ConnectionString = "Server=.;Database=Test;Uid=sa;Pwd=********;";
        private static readonly Hashtable _HashTableName = new Hashtable(); //表名快取
        private static readonly Hashtable _HashKey = new Hashtable();       //主鍵快取

        /// <summary>
        /// 資料庫連接
        /// </summary>
        /// <returns></returns>
        public static SqlConnection GetConnection()
        {
            SqlConnection conn = new SqlConnection(ConnectionString);
            return conn;
        }

        /// <summary>
        /// 新增
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="entity"></param>
        /// <returns></returns>
        public static int Insert<TEntity>(TEntity entity) where TEntity : class
        {
            string strTableName = "";                                   //表名
            string strInsertSQL = "INSERT INTO {0} ({1}) VALUES ({2})"; //SQL拼接陳述句

            //獲取表名
            strTableName = GetTableName(entity);

            //獲取欄位串列及值串列
            StringBuilder strFields = new StringBuilder();
            StringBuilder strValues = new StringBuilder();
            List<SqlParameter> paraList = new List<SqlParameter>();

            PropertyInfo[] infos = entity.GetType().GetProperties();
            DataFieldAttribute dfAttr = null;
            object[] dfAttrs;

            int i = 0;
            foreach (PropertyInfo info in infos)
            {
                dfAttrs = info.GetCustomAttributes(typeof(DataFieldAttribute), false);
                if (dfAttrs.Length > 0)
                {
                    dfAttr = dfAttrs[0] as DataFieldAttribute;
                    if (dfAttr is DataFieldAttribute)
                    {
                        //自增欄位不作處理
                        if (dfAttr.IsIdentity) continue;

                        strFields.Append(i > 0 ? "," + dfAttr.FieldName : dfAttr.FieldName);
                        strValues.Append(i > 0 ? "," + "@" + dfAttr.FieldName : "@" + dfAttr.FieldName);
                        i++;
                        paraList.Add(new SqlParameter("@" + dfAttr.FieldName, info.GetValue(entity, null)));
                    }
                }
            }

            //格式化SQL拼接陳述句
            string[] args = new string[] { strTableName, strFields.ToString(), strValues.ToString() };
            strInsertSQL = string.Format(strInsertSQL, args);

            //執行結果
            int result = 0;
            try
            {
                using (SqlConnection conn = GetConnection())
                {
                    conn.Open();
                    using (SqlCommand cmd = new SqlCommand())
                    {
                        cmd.CommandText = strInsertSQL;
                        cmd.CommandType = CommandType.Text;
                        cmd.Connection = conn;
                        if (paraList != null)
                        {
                            foreach (SqlParameter param in paraList)
                            {
                                cmd.Parameters.Add(param);
                            }
                        }
                        result = cmd.ExecuteNonQuery();
                    }
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.ToString());
            }

            //回傳影響行數
            return result;
        }

        /// <summary>
        /// 洗掉
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="condition"></param>
        /// <returns></returns>
        public static int Delete<TEntity>(string condition) where TEntity : class, new()
        {
            string strTableName = "";                           //表名
            string strDeleteSQL = "DELETE FROM {0} WHERE {1}";  //SQL拼接陳述句

            //獲取表名
            strTableName = GetTableName(new TEntity());

            //格式化SQL拼接陳述句
            string[] args = new string[] { strTableName, condition };
            strDeleteSQL = string.Format(strDeleteSQL, args);

            //執行結果
            int result = 0;
            try
            {
                using (SqlConnection conn = GetConnection())
                {
                    conn.Open();
                    using (SqlCommand cmd = new SqlCommand())
                    {
                        cmd.CommandText = strDeleteSQL;
                        cmd.CommandType = CommandType.Text;
                        cmd.Connection = conn;
                        result = cmd.ExecuteNonQuery();
                    }
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.ToString());
            }

            //回傳影響行數
            return result;
        }

        /// <summary>
        /// 更新
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="entity"></param>
        /// <returns></returns>
        public static int Update<TEntity>(TEntity entity) where TEntity : class
        {
            string strTableName = "";                               //表名
            string strUpdateSQL = "UPDATE {0} SET {1} WHERE {2}";   //SQL拼接陳述句
            string strKey = "";                                     //主鍵
            string strWhere = "";                                   //條件

            //獲取表名及主鍵
            strTableName = GetTableName(entity);
            strKey = GetKey(entity);

            //獲取更新串列
            StringBuilder strSET = new StringBuilder();
            List<SqlParameter> paraList = new List<SqlParameter>();

            PropertyInfo[] infos = entity.GetType().GetProperties();
            DataFieldAttribute dfAttr = null;
            object[] dfAttrs;

            int i = 0;
            foreach (PropertyInfo info in infos)
            {
                dfAttrs = info.GetCustomAttributes(typeof(DataFieldAttribute), false);
                if (dfAttrs.Length > 0)
                {
                    dfAttr = dfAttrs[0] as DataFieldAttribute;
                    if (dfAttr is DataFieldAttribute)
                    {
                        //條件處理
                        if (dfAttr.FieldName == strKey)
                        {
                            strWhere = strKey + "=" + info.GetValue(entity, null);
                        }

                        //自增欄位不作處理
                        if (dfAttr.IsIdentity) continue;

                        strSET.Append(i > 0 ? "," + dfAttr.FieldName + "=@" + dfAttr.FieldName : dfAttr.FieldName + "=@" + dfAttr.FieldName);
                        i++;
                        paraList.Add(new SqlParameter("@" + dfAttr.FieldName, info.GetValue(entity, null)));
                    }
                }
            }

            //格式化SQL拼接陳述句
            string[] args = new string[] { strTableName, strSET.ToString(), strWhere };
            strUpdateSQL = string.Format(strUpdateSQL, args);

            //執行結果
            int result = 0;
            try
            {
                using (SqlConnection conn = GetConnection())
                {
                    conn.Open();
                    using (SqlCommand cmd = new SqlCommand())
                    {
                        cmd.CommandText = strUpdateSQL;
                        cmd.CommandType = CommandType.Text;
                        cmd.Connection = conn;
                        if (paraList != null)
                        {
                            foreach (SqlParameter param in paraList)
                            {
                                cmd.Parameters.Add(param);
                            }
                        }
                        result = cmd.ExecuteNonQuery();
                    }
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.ToString());
            }

            //回傳影響行數
            return result;
        }

        /// <summary>
        /// 查詢
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="condition"></param>
        /// <returns></returns>
        public static List<TEntity> Query<TEntity>(string condition) where TEntity : class, new()
        {
            string strTableName = "";                               //表名
            string strSelectSQL = "SELECT * FROM {0} WHERE {1}";    //SQL拼接陳述句
            List<TEntity> list = new List<TEntity>();               //物體串列

            //獲取表名
            strTableName = GetTableName(new TEntity());

            //格式化SQL拼接陳述句
            string[] args = new string[] { strTableName, condition };
            strSelectSQL = string.Format(strSelectSQL, args);

            //獲取物體串列
            PropertyInfo[] infos = typeof(TEntity).GetProperties();
            DataFieldAttribute dfAttr = null;
            object[] dfAttrs;
            try
            {
                using (SqlConnection conn = GetConnection())
                {
                    conn.Open();
                    using (SqlCommand cmd = new SqlCommand(strSelectSQL, conn))
                    {
                        using (SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.CloseConnection))
                        {
                            while (dr.Read())
                            {
                                TEntity entity = new TEntity();
                                foreach (PropertyInfo info in infos)
                                {
                                    dfAttrs = info.GetCustomAttributes(typeof(DataFieldAttribute), false);
                                    if (dfAttrs.Length > 0)
                                    {
                                        dfAttr = dfAttrs[0] as DataFieldAttribute;

                                        if (dfAttr is DataFieldAttribute)
                                        {
                                            info.SetValue(entity, dr[dfAttr.FieldName]);
                                        }
                                    }
                                }
                                list.Add(entity);
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.ToString());
            }

            //回傳物體串列
            return list;
        }

        /// <summary>
        /// 根據物體回傳表名
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public static string GetTableName<TEntity>(TEntity entity) where TEntity : class
        {
            Type entityType = entity.GetType();
            string strTableName = Convert.ToString(_HashTableName[entityType.FullName]);

            if (strTableName == "")
            {
                if (entityType.GetCustomAttributes(typeof(DataTableAttribute), false)[0] is DataTableAttribute dtAttr)
                {
                    strTableName = dtAttr.TableName;
                }
                else
                {
                    throw new Exception(entityType.ToString() + "未設定DataTable特性,");
                }

                _HashTableName[entityType.FullName] = strTableName;
            }

            return strTableName;
        }

        /// <summary>
        /// 根據物體回傳主鍵
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public static string GetKey<TEntity>(TEntity entity) where TEntity : class
        {
            Type entityType = entity.GetType();
            string strKey = Convert.ToString(_HashKey[entityType.FullName]);

            if (strKey == "")
            {
                if (entityType.GetCustomAttributes(typeof(DataTableAttribute), false)[0] is DataTableAttribute dtAttr)
                {
                    strKey = dtAttr.Key;
                }
                else
                {
                    throw new Exception(entityType.ToString() + "未設定DataTable特性,");
                }

                _HashKey[entityType.FullName] = strKey;
            }

            return strKey;
        }
    }
}
DBHelper.cs

    五、運行測驗

    新建一個控制臺程式:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using LinkTo.ORM.Model;

namespace LinkTo.ORM.Client
{
    class Program
    {
        static void Main()
        {
            //新增
            Person insertPerson = new Person
            {
                Name = "Hello",
                Age = 18,
                Gender = "male"
            };
            int insertResult = DBHelper.Insert(insertPerson);
            Console.WriteLine($"共新增了 {insertResult} 條記錄,");

            //更新
            List<Person> updateList = DBHelper.Query<Person>("Name='Hello'");
            int updateResult = 0;
            if (updateList.Count > 0)
            {
                foreach (var item in updateList)
                {
                    Person updatePerson = item;
                    updatePerson.Age = 19;
                    updateResult += DBHelper.Update(updatePerson);
                }
            }
            Console.WriteLine($"共更新了 {updateResult} 條記錄,");

            //查詢
            List<Person> selectList = DBHelper.Query<Person>("Name='Hello'");
            if (selectList.Count > 0)
            {
                foreach (var item in selectList)
                {
                    Console.WriteLine("person.Name = " + item.Name);
                    Console.WriteLine("person.Age = " + item.Age);
                    Console.WriteLine("person.Gender = " + item.Gender);
                }
            }

            //洗掉
            int deleteResult = DBHelper.Delete<Person>("Name='Hello'");
            Console.WriteLine($"共洗掉了 {deleteResult} 條記錄,");

            Console.Read();
        }
    }
}
Program.cs

    運行結果如下:

 

    原始碼下載

 

    參考自:

    MiniORM

    https://blog.csdn.net/m0_37224390/article/details/81134550

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/46969.html

標籤:C#

上一篇:dotnet 部署 github 的 Action 進行持續集成

下一篇:C# 基礎知識系列- 13 常見類別庫介紹(二)日期時間類

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more