C# 基于SQLite的sql构建器

SQLite 数据类型与C#数据类型对应表

前言

操作数据库时手动拼装sql语句相当麻烦,而且容易拼错。
SQL构建器实际上就是封装的一个可以拼接sql语句的类,使用SQL构建器,可以通过创建一个实例来调用方法生成SQL语句。
本人用过Laravel框架(一个PHP框架),感觉它的sql构建器用着比较舒服,所以就仿着Laravel中sql构建器的书写方式,实现了一个C#版本。
封装的sql构建器类实现了对数据库的增删改查、事务,用到了泛型类、泛型方法、反射、委托、Lambda表达式、泛型类的静态构造方法进行缓存等知识点。

使用方法

插入一条记录

bool b = DB<Company>.CreateInstance.Insert(new Dictionary<string, object>()
{
    { "Name", "sss" },
    { "CreateTime", null },
    { "LastModifierId",null }
});

插入一条记录,并返回新插入记录的ID

long l = DB<Company>.CreateInstance.InsertGetId(new Dictionary<string, object>()
{
	{ "Name", "xxxxxx" },
  	{ "CreateTime", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") },
  	{ "LastModifierId",66 }
});

根据ID删除一条记录

bool b = DB<Company>.CreateInstance.Delete(75);

删除记录

//指定一个where条件
bool b = DB<Company>.CreateInstance.Where("Id", ">", "2").Delete();

//指定多个where条件,条件之间用AND连接
bool b = DB<Company>.CreateInstance.AndWhere(new string[,]{
			{"Id", ">", "5"},
			{"Id", "<", "10"}
		}).Delete();
		
//指定多个where条件,条件之间用OR连接
bool b = DB<Company>.CreateInstance.OrWhere(new string[,]{
			{"Id", "=", "5"},
			{"Name", "=", "aaa"}
		}).Delete();

修改记录

bool b = DB<Company>.CreateInstance.Where("id", ">", "14").Update(new Dictionary<string, object>()
         {
			{ "Name", "汇邦123" },
			{ "CreateTime", "2022-02-22" },
			{ "LastModifierId","21" }
         });

查询多条记录

List<Company> l1 = DB<Company>.Create().Field("Id", "Name").Where("Id", ">", "1").OrderBy("Id desc").Limit(5).Get();
List<Company> l2 = DB<Company>.Create().Field("Id", "Name").AndWhere(new string[,]{
						{"Id", ">", "5"},
						{"Id", "<", "10"}
					}).OrderBy("Id desc").Limit(5).Get();

根据id,查询一条记录

Company company = DB<Company>.CreateInstance.Find(13);

查询一条记录

Company company = DB<Company>.Create().Field("Id", "Name").Where("Id", ">", "1").OrderBy("Id desc").First();
Company company = DB<Company>.Create().Field("Id", "Name").AndWhere(new string[,]{
						{"Id", ">", "5"},
						{"Id", "<", "10"}
					}).OrderBy("Id desc").First();

查询一条记录的单个字段值

var id = DB<Company>.CreateInstance.Where("Id", ">", "1").OrderBy("Id desc").Value<int?>("LastModifierId");
var id = DB<Company>.CreateInstance.AndWhere(new string[,]{
			{"Id", ">", "5"},
			{"Id", "<", "10"}
		}).OrderBy("Id desc").Value<int?>("LastModifierId");

查询符合条件的记录数

long l = DB<Company>.CreateInstance.Where("Id", ">", "1").Count();
long l = DB<Company>.CreateInstance.AndWhere(new string[,]{
			{"Id", ">", "5"},
			{"Id", "<", "10"}
		}).Count();

根据传入的sql语句查询(无参数)

DataTable dt = DB_STATIC.Select("select * from company");

根据传入的sql语句查询(有参数)

string sql = "select * from company where id=@Id and name=@Name";
DataTable dt = DB_STATIC.Select(sql, new Dictionary<string, object>()
		         {
		         	{ "Id", 1 },
					{ "Name", "汇邦123" }
				 });

事务

  bool b = DB_STATIC.Transaction(() =>
           {
               bool b1 = DB<Company>.CreateInstance.Where("Id", "=", "1").Update(new Dictionary<string, object>()
                {
                    { "Name", "ff" },
                    { "CreateTime", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") },
                    { "LastModifierId",66 }
                 });
                long l = DB<Company>.CreateInstance.InsertGetId(new Dictionary<string, object>()
                {
                    { "Name", "xxxxxx" },
                    { "CreateTime", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") },
                    { "LastModifierId",66 }
                 });
                
                bool b2 = DB<Company>.CreateInstance.Delete(75);
            });

DAL层

DB.cs

using DAL.Mapping;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SQLite;
using System.Linq;
using System.Reflection;

namespace DAL
{

    public class DB<T> where T : BaseModel, new()
    {
        // 非静态字段
        private string field = "";
        private string where = "";
        private string orderBy = "";
        private string limit = "";
        private string[] fieldArr;
        private List<SQLiteParameter> sqlParameterList = new List<SQLiteParameter>();

        // 静态字段
        private static string tableName = null;

        public static DB<T> CreateInstance
        {
            get
            {
                return new DB<T>();
            }
        }

        public static DB<T> Create()
        {
            return new DB<T>();
        }
        //public DB<T> Table(string tableName)
        //{
        //    this.tableName = tableName;
        //    return this;
        //}
        static DB()
        {
            Type type = typeof(T);
            DB<T>.tableName = type.GetMappingName<Type>();
        }
        private DB()
        {

        }

        public DB<T> AndWhere(string[,] whereArr)
        {
            this.WhereAndOr("AND", whereArr);
            return this;
        }
        public DB<T> OrWhere(string[,] whereArr)
        {
            this.WhereAndOr("OR", whereArr);
            return this;
        }

        private void WhereAndOr(string strAndOr, string[,] whereArr)
        {
            if (!string.IsNullOrEmpty(this.where))
            {
                throw new Exception("Where()、AndWhere()、OrWhere()方法只能调用其中一个,且只能调用一次");
            }

            List<string> listWhere = new List<string>();

            for (int i = 0; i < whereArr.GetLength(0); i++)
            {
                listWhere.Add($"\"{whereArr[i, 0]}\"{whereArr[i, 1]}@{whereArr[i, 0]}_Y{i}");
                sqlParameterList.Add(new SQLiteParameter($"@{whereArr[i, 0]}_Y{i}", (object)whereArr[i, 2] ?? DBNull.Value));
            }
            this.where = string.Join($" {strAndOr} ", listWhere);
        }

        public DB<T> Where(params string[] whereArr)
        {
            if (!string.IsNullOrEmpty(this.where))
            {
                throw new Exception("Where()、AndWhere()、OrWhere()方法只能调用其中一个,且只能调用一次");
            }

            this.where = $"\"{whereArr[0]}\"{whereArr[1]}@{whereArr[0]}_Y";
            sqlParameterList.Add(new SQLiteParameter($"@{whereArr[0]}_Y", (object)whereArr[2] ?? DBNull.Value));
            return this;
        }

        public DB<T> OrderBy(string orderBy)
        {
            this.orderBy = orderBy;
            return this;
        }

        public DB<T> Limit(object limit)
        {
            this.limit = limit.ToString();
            return this;
        }

        public DB<T> Field(params string[] fieldArr)
        {
            this.fieldArr = fieldArr;
            this.field = string.Join(",", fieldArr.Select(item => $"\"{item}\""));
            return this;
        }

        /// <summary>
        /// 插入一条数据
        /// </summary>
        /// <param name="dic"></param>
        /// <returns></returns>
        public bool Insert(Dictionary<string, object> dic)
        {
            List<SQLiteParameter> sqlParameterList;
            string sql;
            this.InsertBuildSql(dic, out sqlParameterList, out sql);
            if (DB_STATIC.cmd == null)
            {
                return ExecuteSql<bool>(sql, sqlParameterList.ToArray(), command =>
                {
                    int iResult = command.ExecuteNonQuery();
                    return iResult == 1;
                });
            }
            else
            {
                DB_STATIC.cmd.CommandText = sql;
                DB_STATIC.cmd.Parameters.Clear();
                DB_STATIC.cmd.Parameters.AddRange(sqlParameterList.ToArray());
                int iResult = DB_STATIC.cmd.ExecuteNonQuery();
                return iResult == 1;
            }
        }

        private void InsertBuildSql(Dictionary<string, object> dic, out List<SQLiteParameter> sqlParameterList, out string sql)
        {
            sqlParameterList = new List<SQLiteParameter>();
            foreach (var item in dic)
            {
                //    b ? x : y
                //如果 X 和 Y 的类型相同,则此类型为该条件表达式的类型。
                //否则,如果存在从 X 到 Y 的隐式转换,但不存在从 Y 到 X 的隐式转换,则 Y 为条件表达式的类型。
                //否则,如果存在从 Y 到 X 的隐式转换,但不存在从 X 到 Y 的隐式转换,则 X 为条件表达式的类型。
                //否则,无法确定条件表达式的类型
                sqlParameterList.Add(new SQLiteParameter($"@{item.Key}", item.Value ?? DBNull.Value));
            }

            string fieldString = string.Join(",", dic.Keys.Select(p => $"\"{p}\""));
            string valueString = string.Join(",", dic.Keys.Select(p => $"@{p}"));
            sql = $"INSERT INTO \"{DB<T>.tableName}\" ({fieldString}) VALUES ({valueString})";
        }

        /// <summary>
        /// 插入一条数据并返回id
        /// </summary>
        /// <param name="dic"></param>
        /// <returns>新插入数据的id</returns>
        public long InsertGetId(Dictionary<string, object> dic)
        {
            List<SQLiteParameter> sqlParameterList;
            string sql;
            this.InsertBuildSql(dic, out sqlParameterList, out sql);
   
            sql += $";SELECT MAX(ROWID) FROM \"{DB<T>.tableName}\"";
            if (DB_STATIC.cmd == null)
            {
                return ExecuteSql<long>(sql, sqlParameterList.ToArray(), command =>
                {
                    long iResult = (long)command.ExecuteScalar();
                    return iResult;
                });
            }
            else
            {
                DB_STATIC.cmd.CommandText = sql;
                DB_STATIC.cmd.Parameters.Clear();
                DB_STATIC.cmd.Parameters.AddRange(sqlParameterList.ToArray());
                long iResult = (long)DB_STATIC.cmd.ExecuteScalar();
                return iResult;
            }
        }

        /// <summary>
        /// 删除一条数据(根据id)
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public bool Delete<X>(X id)
        {
            if (!string.IsNullOrEmpty(this.where))
            {
                throw new Exception("根据Id删除时,不需要指定Where条件");
            }
            this.Where("Id", "=", Convert.ToInt64(id).ToString());
            return Delete();
        }

        /// <summary>
        /// 删除数据
        /// </summary>
        /// <returns></returns>
        public bool Delete()
        {
            if (string.IsNullOrEmpty(this.where))
            {
                throw new Exception("执行Delete操作必须有Where条件");
            }
            string sql = $"DELETE FROM \"{DB<T>.tableName}\"";
            sql += $" WHERE {this.where}";

            if (DB_STATIC.cmd == null)
            {
                return ExecuteSql<bool>(sql, sqlParameterList.ToArray(), command =>
                {
                    int iResult = command.ExecuteNonQuery();
                    return iResult > 0;
                });
            }
            else
            {
                DB_STATIC.cmd.CommandText = sql;
                DB_STATIC.cmd.Parameters.Clear();
                DB_STATIC.cmd.Parameters.AddRange(sqlParameterList.ToArray());
                int iResult = DB_STATIC.cmd.ExecuteNonQuery();
                return iResult > 0;
            }
        }

        /// <summary>
        /// 更新数据
        /// </summary>
        /// <param name="dic"></param>
        /// <returns></returns>
        public bool Update(Dictionary<string, object> dic)
        {
            if (string.IsNullOrEmpty(this.where))
            {
                throw new Exception("执行Update操作必须有Where条件");
            }

            foreach (var item in dic)
            {
                sqlParameterList.Add(new SQLiteParameter($"@{item.Key}_X", item.Value ?? DBNull.Value));
            }

            string keyValString = string.Join(",", dic.Keys.Select(p => $"\"{p}\"=@{p}_X"));
            string sql = $"UPDATE \"{DB<T>.tableName}\" SET {keyValString}";

            sql += $" WHERE {this.where}";
            if (DB_STATIC.cmd == null)
            {
                return ExecuteSql<bool>(sql, sqlParameterList.ToArray(), command =>
                {
                    //经测试发现:在sqlite中update操作就算没做任何修改也会返回受影响的行数(不会返回0);mysql则不是
                    int iResult = command.ExecuteNonQuery();
                    return iResult > 0;
                });
            }
            else
            {
                DB_STATIC.cmd.CommandText = sql;
                DB_STATIC.cmd.Parameters.Clear();
                DB_STATIC.cmd.Parameters.AddRange(sqlParameterList.ToArray());
                int iResult = DB_STATIC.cmd.ExecuteNonQuery();
                return iResult > 0;
            }
        }

        /// <summary>
        /// 查询多条数据
        /// </summary>
        /// <returns></returns>
        public List<T> Get()
        {
            Type type = typeof(T);

            if (string.IsNullOrEmpty(this.field))
            {
                this.field = string.Join(",", type.GetProperties().Select(prop => $"\"{prop.Name}\""));
                this.fieldArr = type.GetProperties().Select(prop => prop.Name).ToArray();
            }

            string sql = $"SELECT {this.field} FROM \"{DB<T>.tableName}\"";
            if (!string.IsNullOrEmpty(this.where))
            {
                sql += $" WHERE {this.where}";
            }
            if (!string.IsNullOrEmpty(this.orderBy))
            {
                sql += $" ORDER BY {this.orderBy}";
            }
            if (!string.IsNullOrEmpty(this.limit))
            {
                sql += $" LIMIT {this.limit}";
            }

            return ExecuteSql<List<T>>(sql, sqlParameterList.ToArray(), command =>
           {
               SQLiteDataReader reader = command.ExecuteReader();
               List<T> list = new List<T>();
               //T t = Activator.CreateInstance<T>();
               while (reader.Read())
               {
                   T t = new T();

                   foreach (string propName in this.fieldArr)
                   {
                       PropertyInfo prop = type.GetProperty(propName);
                       prop.SetValue(t, reader[propName] is DBNull ? null : reader[propName]);
                   }

                   list.Add(t);
               }
               reader.Close();
               return list;
           });
        }

        /// <summary>
        /// 查询一条数据(根据id)
        /// </summary>
        /// <param name="id">主键Id</param>
        /// <returns>实体对象 或 null</returns>
        public T Find<X>(X id)
        {
            Type type = typeof(T);

            if (string.IsNullOrEmpty(this.field))
            {
                this.field = string.Join(",", type.GetProperties().Select(prop => $"\"{prop.Name}\""));
                this.fieldArr = type.GetProperties().Select(prop => prop.Name).ToArray();
            }

            string sql = $"SELECT {this.field} FROM \"{DB<T>.tableName}\" WHERE \"Id\"=@Id";
            SQLiteParameter[] sqlParameter = { new SQLiteParameter($"@Id", Convert.ToInt64(id)) };
            return ExecuteSql<T>(sql, sqlParameter, command =>
            {
                using (SQLiteDataReader reader = command.ExecuteReader())
                {
                    //SQLiteDataReader 在使用完成后必须手动释放(using或close),否则会出问题;
                    if (reader.Read())
                    {
                        //T t = Activator.CreateInstance<T>();
                        T t = new T();

                        foreach (string propName in this.fieldArr)
                        {
                            PropertyInfo prop = type.GetProperty(propName);
                            prop.SetValue(t, reader[propName] is DBNull ? null : reader[propName]);
                        }

                        return t;
                    }
                    else
                    {
                        return default(T);
                    }
                }
            });
        }

        /// <summary>
        /// 查询一条数据
        /// </summary>
        /// <param name="id">主键Id</param>
        /// <returns>实体对象 或 null</returns>
        public T First()
        {
            Type type = typeof(T);

            if (string.IsNullOrEmpty(this.field))
            {
                this.field = string.Join(",", type.GetProperties().Select(prop => $"\"{prop.Name}\""));
                this.fieldArr = type.GetProperties().Select(prop => prop.Name).ToArray();
            }

            string sql = $"SELECT {this.field} FROM \"{DB<T>.tableName}\"";

            if (!string.IsNullOrEmpty(this.where))
            {
                sql += $" WHERE {this.where}";
            }

            if (!string.IsNullOrEmpty(this.orderBy))
            {
                sql += $" ORDER BY {this.orderBy}";
            }

            sql += $" LIMIT 1";


            return ExecuteSql<T>(sql, sqlParameterList.ToArray(), command =>
            {
                using (SQLiteDataReader reader = command.ExecuteReader())
                {
                    if (reader.Read())
                    {

                        //T t = Activator.CreateInstance<T>();
                        T t = new T();

                        foreach (string propName in this.fieldArr)
                        {
                            PropertyInfo prop = type.GetProperty(propName);
                            prop.SetValue(t, reader[propName] is DBNull ? null : reader[propName]);
                        }

                        return t;
                    }
                    else
                    {
                        return default(T);
                    }
                }
            });
        }

        /// <summary>
        /// 从结果中获取单个值
        /// </summary>
        /// <typeparam name="S"></typeparam>
        /// <param name="oneFieldName"></param>
        /// <returns></returns>
        public S Value<S>(string oneFieldName)
        {
            string sql = $"SELECT \"{oneFieldName}\" FROM \"{DB<T>.tableName}\"";
            if (!string.IsNullOrEmpty(this.where))
            {
                sql += $" WHERE {this.where}";
            }
            if (!string.IsNullOrEmpty(this.orderBy))
            {
                sql += $" ORDER BY {this.orderBy}";
            }

            sql += $" LIMIT 1";


            return ExecuteSql<S>(sql, sqlParameterList.ToArray(), command =>
            {
                object obj = command.ExecuteScalar();
                if (obj is DBNull || obj == null)
                {
                    return default(S);
                }
                else
                {
                    return (S)obj;
                }
            });
        }

        public long Count()
        {
            string sql = $"SELECT COUNT(1) FROM \"{DB<T>.tableName}\"";
            if (!string.IsNullOrEmpty(this.where))
            {
                sql += $" WHERE {this.where}";
            }

            return ExecuteSql<long>(sql, sqlParameterList.ToArray(), command =>
           {
               return (long)command.ExecuteScalar();
           });
        }


        private H ExecuteSql<H>(string sql, SQLiteParameter[] sqlParameter, Func<SQLiteCommand, H> func)
        {
            using (SQLiteConnection conn = new SQLiteConnection(DB_STATIC.ConnectionString))
            {
                SQLiteCommand command = new SQLiteCommand(sql, conn);
                command.Parameters.AddRange(sqlParameter);
                conn.Open();
                return func.Invoke(command);
            }
        }
    }
}

DB_STATIC.cs

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SQLite;

namespace DAL
{

    public static class DB_STATIC
    {
        // 静态字段
        public static SQLiteCommand cmd = null;
        public static readonly string ConnectionString = "data source=./DB_test.db;version=3;";

        /// <summary>
        /// 根据传入的sql语句查询(无参数)
        /// </summary>
        /// <param name="sqlStr">sql字符串</param>
        /// <returns>DataTable类型</returns>
        public static DataTable Select(string sqlStr)
        {
            Dictionary<string, object> dic = new Dictionary<string, object>();
            return Select(sqlStr, dic);
        }

        /// <summary>
        /// 根据传入的sql语句查询(有参数)
        /// </summary>
        /// <param name="sqlStr">sql字符串</param>
        /// <param name="dic"></param>
        /// <returns>DataTable类型</returns>
        public static DataTable Select(string sqlStr, Dictionary<string, object> dic)
        {
            List<SQLiteParameter> sqlParameterList = new List<SQLiteParameter>();
            foreach (var item in dic)
            {
                sqlParameterList.Add(new SQLiteParameter($"@{item.Key}", item.Value ?? DBNull.Value));
            }

            using (SQLiteConnection conn = new SQLiteConnection(ConnectionString))
            {
                using (SQLiteDataAdapter da = new SQLiteDataAdapter(sqlStr, conn))
                {
                    da.SelectCommand.Parameters.AddRange(sqlParameterList.ToArray());
                    DataTable dt = new DataTable();
                    da.Fill(dt);
                    return dt;
                }
            }
        }


        /// <summary>
        /// 事务
        /// </summary>
        /// <returns></returns>
        public static bool Transaction(Action action)
        {
            using (SQLiteConnection conn = new SQLiteConnection(ConnectionString))
            {
                conn.Open();
                SQLiteTransaction tran = conn.BeginTransaction();//SQLite中,此行执行完会锁库
                DB_STATIC.cmd = new SQLiteCommand();
                DB_STATIC.cmd.Transaction = tran;
                try
                {
                    action.Invoke();
                    tran.Commit();
                    return true;
                }
                catch (Exception ex)
                {
                    //Console.WriteLine(ex.Message);
                    //throw ex;
                    tran.Rollback();
                    return false;
                }
                finally
                {
                    DB_STATIC.cmd.Dispose();
                    DB_STATIC.cmd = null;
                }
            }
        }

    }
}

BaseModel.cs

using DAL.Mapping;

namespace DAL
{
    public class BaseModel
    {
        [KeyAttribute]
        public long Id { get; set; }
    }
}

MappingAttributeExtend.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace DAL.Mapping
{
    public static class MappingAttributeExtend
    {
        /// <summary>
        /// 把下面两个方法整合为一个泛型方法
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="type"></param>
        /// <returns></returns>
        public static string GetMappingName<T>(this T member) where T : MemberInfo
        {
            if (member.IsDefined(typeof(AbstractMappingAttribute), true))
            {
                AbstractMappingAttribute attribute = member.GetCustomAttribute<AbstractMappingAttribute>();
                return attribute.GetMappingName();
            }
            else
            {
                return member.Name;
            }
        }

        public static string GetTableName(this Type type)
        {
            if (type.IsDefined(typeof(TableMappingAttribute), true))
            {
                TableMappingAttribute attribute = type.GetCustomAttribute<TableMappingAttribute>();
                return attribute.GetMappingName();
            }
            else
            {
                return type.Name;
            }
        }

        public static string GetColumnName(this PropertyInfo prop)
        {
            if (prop.IsDefined(typeof(ColumnMappingAttribute), true))
            {
                ColumnMappingAttribute attribute = prop.GetCustomAttribute<ColumnMappingAttribute>();
                return attribute.GetMappingName();
            }
            else
            {
                return prop.Name;
            }
        }

        public static IEnumerable<PropertyInfo> NotKey(this IEnumerable<PropertyInfo> propertyInfos)
        {
            return propertyInfos.Where(p => !p.IsDefined(typeof(KeyAttribute), true));
        }
    }
}

AbstractMappingAttribute.cs

using System;

namespace DAL.Mapping
{
    public abstract class AbstractMappingAttribute : Attribute
    {
        private string _MappingName = null;
        public AbstractMappingAttribute(string name)
        {
            this._MappingName = name;
        }
        public string GetMappingName()
        {
            return this._MappingName;
        }
    }
}

ColumnMappingAttribute.cs

using System;

namespace DAL.Mapping
{
    [AttributeUsage(AttributeTargets.Property)]
    public class ColumnMappingAttribute : AbstractMappingAttribute
    {
        public ColumnMappingAttribute(string name) : base(name)
        {
        }
    }
}

TableMappingAttribute.cs

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

namespace DAL.Mapping
{
    [AttributeUsage(AttributeTargets.Class)]
    public class TableMappingAttribute : AbstractMappingAttribute
    {
        public TableMappingAttribute(string name) : base(name)
        {
        }
    }
}

KeyAttribute.cs

using System;

namespace DAL.Mapping
{
    [AttributeUsage(AttributeTargets.Property)]
    public class KeyAttribute : Attribute
    {
    }
}

Model层

Company.cs

using DAL;
using DAL.Mapping;
using System;
namespace Model
{
    [TableMappingAttribute("Company")]
    public class Company : BaseModel
    {
        //[ElevenColumnMappingAttribute("Name")]
        public string Name { get; set; }

        public DateTime? CreateTime { get; set; }
        //public string  CreateTime { get; set; }

        public Nullable<int> LastModifierId { get; set; }//Nullable<int> 与 int? 等价 
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值