.Net泛型+反射(超实用的文章)包含:泛型缓存,特性,简单工厂

一.引言

仅供学习参考使用

要求:

1 建立一个数据库,然后执行下面的数据库脚本,会增加两张表 User Company,大家可以去表里面自己多加入一些数据

2 建立数据库表映射的基类BaseModel,包括 Id属性
  建立两个子类Model:公司和用户,按照表结构来

3 提供两个泛型的数据库访问方法,用 BaseModel约束
一个是用id去查询单个实体,(只有这一个参数)
一个是查询出数据表的全部数据列表查询(没有参数)

提示:用DataReader去访问数据库,将得到的结果通过反射生成实体对象/集合返回;

4 封装一个方法,能控制台输出任意实体的全部属性和属性值;

5 进阶需求:提供泛型的数据库实体插入、实体更新、ID删除数据的数据库访问方法;

6 进阶需求(可选):欢迎小伙伴儿写个实体自动生成器;

7 进阶需求(可选):将数据访问层抽象,使用简单工厂+配置文件+反射的方式,来提供对数据访问层的使用

8 进阶需求(可选):每个实体类的基础增删改查SQL语句是不变的,用泛型缓存试试!

1 封装一个方法,能控制台输出任意实体的全部属性和属性值  Name:Eleven;
  升级一下,属性名称希望用具体中文描述,而不是实体的属性名,也就是 名称:Eleven;

2 如果数据库的表/字段名称和程序中实体不一致,尝试用特性提供,解决增删改查;
  (数据库是user 程序得是UserModel)
  (数据库是state 程序得是status)

3 通用数据验证,Required(非空)  Mobile(手机号格式) Email(格式) 字符串长度(最大最小)等常见规则;
  支持一个属性多重验证;
  支持返回错误信息;
  支持返回全部错误(一个对象多个属性,可能多个条件都不满足);

4 委托优化,项目中的各种重复代码

5 进阶需求(可选):反射特性的很多东西缓存一下,字典缓存OR泛型缓存
View Code

 二.代码部分

1.项目框架如下:

 .Net 5.0控制台项目

 刚开始可能代码断断续续,后期全部完成后会整理

 CommonTool下的类BaseModel

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Training.CommonTool
{
    public class BaseModel
    {
        public int Id { get; set; }
    }
}
BaseModel

CommonTool下的文件夹AttributeExtend下的类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Training.CommonTool.AttributeExtend
{
    public class MattAbstractAttribute:Attribute
    {
        protected string Name;
        public MattAbstractAttribute(string name)
        {
            this.Name = name;
        }
        public string GetName()
        {
            return this.Name;
        }
    }
}
MattAbstractAttribute
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Training.CommonTool.AttributeExtend
{
    [AttributeUsage(AttributeTargets.Class)]
    public class TableAttribute: MattAbstractAttribute
    {
        public TableAttribute(string tableName):base(tableName)
        {

        }
    }
}
TableAttribute
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Training.CommonTool.AttributeExtend
{
    [AttributeUsage(AttributeTargets.Property)]
    public class ColumnAttribute: MattAbstractAttribute
    {

        public ColumnAttribute(string columnName):base(columnName)
        {

        }
    }
}
ColumnAttribute
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Training.CommonTool.AttributeExtend
{
    /// <summary>
    /// 特性帮助类
    /// </summary>
    public static class AttributeHelper
    {
        
        /// <summary>
        /// 获取特性名称(实体name以及字段name)
        /// </summary>
        /// <param name="memberInfo"></param>
        /// <returns></returns>
        //this代表扩展方法
        public static string GetAttributeName(this MemberInfo memberInfo)
        {
            //判断是否存在
            if (memberInfo.IsDefined(typeof(MattAbstractAttribute),true))
            {
                //GetCustomAttribute获取自定义特性
                MattAbstractAttribute mattAbstract =memberInfo.GetCustomAttribute<MattAbstractAttribute>();
                return mattAbstract.GetName();
            }
            //如果未标记特性,就返回原有名称(tablename/Property)
            return memberInfo.Name;
        }
    }
}
AttributeHelper

IService下的IBaseService接口

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Training.CommonTool;

namespace Training.IService
{
    /// <summary>
    /// 服务层接口
    /// </summary>
    public interface IBaseService
    {
        /// <summary>
        /// 根据id找到某个实体的某一行的数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="id"></param>
        /// <returns></returns>
        T Find<T>(int id) where T : BaseModel;
        /// <summary>
        /// 查找到对应实体的全部数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        List<T> FindAll<T>() where T : BaseModel;
        /// <summary>
        /// 新增接口
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <returns></returns>
        bool Add<T>(T t) where T : BaseModel;
        /// <summary>
        /// 修改接口
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <returns></returns>
        bool Update<T>(T t) where T : BaseModel;
        /// <summary>
        /// 删除接口
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <returns></returns>
        bool Delete<T>(T t) where T : BaseModel;
    }
}
IBaseService

Model层下的类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Training.Model
{
    public class Company
    {
        public string Name { get; set; }

        public DateTime CreateTime { get; set; }
        public int CreatorId { get; set; }
        public int? LastModifierId { get; set; }
        public DateTime? LastModifyTime { get; set; }
    }
}
Company
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Training.CommonTool;
using Training.CommonTool.AttributeExtend;

namespace Training.Model
{
    [Table("User")]
    public class UserModel : BaseModel
    {
        public string Name { get; set; }
        [Column("Account")]
        public string AccountName { get; set; }
        public string Password { get; set; }
        public string Email { get; set; }
        public string Mobile { get; set; }
        public int? CompanyId { get; set; }
        public string CompanyName { get; set; }
        public int State { get; set; }
        public int UserType { get; set; }
        public DateTime? LastLoginTime { get; set; }
        public DateTime CreateTime { get; set; }
        public int CreatorId { get; set; }
        public int? LastModifierId { get; set; }
        public DateTime? LastModifyTime { get; set; }
    }
}
UserModel

Service下的BaseService类

using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Training.CommonTool;
using Training.CommonTool.AttributeExtend;
using Training.IService;

namespace Training.Service
{
    public class BaseService : IBaseService
    {
        /// <summary>
        /// 通用的动作:创建、打开、释放连接 sqlcommand创建
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="func"></param>
        /// <returns></returns>
        private T Command<T>(string sql, Func<SqlCommand, T> func)
        {
            using SqlConnection conn = new(ConfigrationManager.SqlConnectionString);
            SqlCommand sqlCommand = new(sql, conn);
            conn.Open();
            //Invoke是调用一个委托,包含一个入参及出参(两个类型参数不同),
            //我们这里调用的是func委托Func<SqlCommand, T>,入参是SqlCommand,出参是T
            return func.Invoke(sqlCommand);
        }
        public bool Add<T>(T t) where T : BaseModel
        {
            Type type = typeof(T);
            //1.首先获取实体的所有字段
            //2.将这些字段拼接成sqlparameter需要的参数
            //3.存在部分字段是空值,所以需要用??判断,如果是空值就返回DBnull.Value,不是空值就返回原本的值
            //4.将添加语句从TSqlHelper类中读取过来
            //5.调用封装好的command方法,将参数及值添加进去
            //6.判断最终的结果成功与否
            var prop = type.GetProperties().Where(s => !s.Name.Equals("Id"));
            var param = prop.Select(s => new SqlParameter($@"{s.GetAttributeName()}", s.GetValue(t) ?? DBNull.Value)).ToArray();
            string sql = $"{TSqlHelper<T>.Addsql}";
            //因为需要写不止一句代码,所以得带方法体{}
            //??表示为空就是DBNull.Value,不为空就返回原来的值
            //因为AddRange方法需要参数SqlParameter[]数组类型的,所以需要调用.ToArray()将返回值转为数组
            //SqlParameter需要传入两个参数,一个是参数名称,另一个是参数值
            //因为我们最终返回true,所以编译器推测返回值是bool,所以就不需要写command返回值类型
            return Command(sql, sqlcommand =>
            {
                sqlcommand.Parameters.AddRange(param);
                int result = sqlcommand.ExecuteNonQuery();
                if (result == 0)
                    throw new Exception("新增异常");
                return true;
            });
        }

        public T Find<T>(int id) where T : BaseModel
        {
            throw new NotImplementedException();
        }

        public List<T> FindAll<T>() where T : BaseModel
        {
            throw new NotImplementedException();
        }

        public bool Update<T>(T t) where T : BaseModel
        {
            throw new NotImplementedException();
        }

        public bool Delete<T>(T t) where T : BaseModel
        {
            throw new NotImplementedException();
        }
    }
}
BaseService

Training下的ConfigrationManager类

using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Training
{
    /// <summary>
    /// 获取配置文件帮助类
    /// </summary>
    public static class ConfigrationManager
    {
        //有了IOC再去注入--容器单例
        static ConfigrationManager()
        {
            var builder = new ConfigurationBuilder()
                    .SetBasePath(Directory.GetCurrentDirectory())
                    .AddJsonFile("appsetting.json");
            IConfigurationRoot configuration = builder.Build();
            _SqlConnectionString = configuration["ConnectionString"];
        }
        private static string _SqlConnectionString = null;
        public static string SqlConnectionString
        {
            get
            {
                return _SqlConnectionString;
            }
        }
    }
}
ConfigrationManager

Training下的appsetting.json文件

{
  "ConnectionString": "server=.;uid=sa;pwd=010806wpz.;database=Work;MultipleActiveResultSets=True"
}
appsetting.json

泛型缓存

Service下的类TSqlHelper(泛型类)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Training.CommonTool;
using Training.CommonTool.AttributeExtend;

namespace Training.Service
{
    /// <summary>
    /// crud语句泛型类
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public  static class TSqlHelper<T> where T:BaseModel
    {
        //泛型约束是为了保证传进来的类型是我们需要的,以免报错类型错误的bug
        //当类与构造函数都为静态时,在调用这个静态类时,会先执行静态构造函数,保存到内存中,在下次调用时直接取,不需要执行,这样就加快了程序的执行效率,提升性能,
        //这个类是泛型静态类时,传入什么类型就会保存什么类型,下次调用其他类型时,会继续执行静态构造函数,无影响
        //因为crud语句需要从这个类里面取,所以需要将他们访问限制设置成公有,方便其他类调用(public)
        public static string Addsql = null;
        public static string Selectsql = null;
        public static string Updatesql = null;
        public static string Deletesql = null;
        //因为tablename以及类型只在内部类中使用,所以不需要公开,访问限制就写为private
        private static Type type = typeof(T);
        //根据泛型传进来的类获取表名
        private static string TableName = type.GetAttributeName();
        //泛型缓存
        static TSqlHelper()
        {
            //string.Join是用来分隔的,第一个以及最后一个字段不会加',',其他都会加','
            //用[]是为了避免表名及字段是关键字
            //一个实体的所有字段
            string columnnName = string.Join(",", type.GetProperties().Where(s=>!s.Name.Equals("Id")).Select(s=>$"[{s.GetAttributeName()}]"));
            //columnValue用@是为了防止sql注入
            //where作用是为了排除id字段,因为id字段数据库设为自增,所以参数和值都将id字段去掉
            //字段值(真正的值会在调用时传入)
            string columnValue = string.Join(",", type.GetProperties().Where(s => !s.Name.Equals("Id")).Select(s=>$"@{s.GetAttributeName()}"));
            string editNameValue = string.Join(",",type.GetProperties()
                .Where(s=>!s.Name.Equals("Id"))
                .Select(s=>$"[{s.GetAttributeName()}]=@{s.GetAttributeName()}"));
            Selectsql = $"select [{TableName}] from {columnnName}";
            Addsql = $"insert [{TableName}]({columnnName}) values ({columnValue})";
            Deletesql = $"delete from [{TableName}]";
            Updatesql = $"update [{TableName}] set {editNameValue}";
        }
    }
}
TSqlHelper

 会不断更新,具体解释都在代码注释里了,一起进步啊!

3-5天内更新完,最近自己也在学习

posted @ 2021-09-25 16:41  rookiexwang  阅读(121)  评论(0编辑  收藏  举报