• C# ORM学习笔记:使用特性+反射实现简单ORM


        一、原理与环境

        在生成数据表的实体类时,利用自定义特性,给它打上表及字段的特性,然后使用反射原理,将自定义特性拼接成增、删、改、查对应的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

  • 相关阅读:
    32位和64位的区别
    Git--版本管理的使用及理解
    Maven使用详解
    记录centos7下tomcat部署war包过程
    SSM三大框架整合教程
    Mybatis 框架搭建实例
    Eclipse 出现select type (? = any character,*= any String,Tz=TimeZone)
    JDBC 操作数据库实例
    mysql 常用命令语法
    MySQL下载安装详情教程(Windows)
  • 原文地址:https://www.cnblogs.com/atomy/p/12764967.html
Copyright © 2020-2023  润新知