博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C# .NET更智能的数据库操作的封装完整版(重构)
阅读量:5037 次
发布时间:2019-06-12

本文共 23714 字,大约阅读时间需要 79 分钟。

前述:

  第一次发表文章,不过是对数据库简单的封装,主要是阐述下思路。那么在上篇文章,在大家的指导下和提出意见,并自己对代码进行了思考。在这两天我重构了新的框架,我觉得我写的可以称得上框架,为什么?请大家往下看。不过在项目中没有很多注释。笔者除了课余学习时候,大部分时间在完成学校的功课,没有许多时间,所以也就偷下懒,请大家体谅。

  这次框架分为几个部分:拼接数据库语句、数据库执行、数据库连接控制、异常类、用户使用的DbHelper。等下我回用文字和图还分析这个部分。经过重构后,类增多了,而且能够极大的支持开闭原则,我应该说就是与实际数据库无关,而且在上层使用中,不需要在引用system.Data.SqlClient这样实际访问的东西。虽然笔者只写了sql server的实例,但是如果扩展其他的数据库,也无需大规模的修改旧的代码,并且使用参数,能够防止注入攻击,支持事务。

  好,先看怎么使用框架。

DbHelper helper = new DbHelper();            helper.createConnection("MyConnection","Data Source=CN-20161106HMJI;Initial Catalog=ShopInfo;Integrated Security=True",DbType.SQL);                        PlaceInfo model = helper.ExcuteString(o => o.From("PlaceInfo").Select().AndWhere("SAddNo", 1)).ToModel
(); Console.Read();

  上面是使用的一个例子,创建连接字符串,然后查询获取实例,已经没有打开数据库,或者是command的语句,使用起来是否十分简单,上面这句运行没有问题的,因为框架灵活度太大,测试的话不能所有都包含,这也是没办法,接下来跟着文章,一步步分析。

  上次说,链式编程很好用,所以这次同样是链式编程,但这次更为强大。大家知道,dal的链式编程,主要是得到数据,而得到数据无非是对数据库查询语言进行封装。所以,在框架上,我封装了一个拼接语句的类,里面包含了我认为比较常用的数据库语句,支持order by。还有最强大的是,能够支持嵌套查询!也就是封装的sql语句可以是

select * from tableName where Id in(select id from tablename where ...)...这样子的。使用起来十分的方便。而且还有排序order by,等,可以在使用这套框架封装更使用的方法。

  第二个新增的是连接控制,这个是这套框架的关键,因为框架不能占用内存,所以无论在拼接查询语句,还是在执行部分,都没有对数据库创建的语句,而是采用注入式,通过连接控制类,创建好数据库连接后,注入到所需要的部分中。而且这里控制了最耗性能的反射,对模型中的属性进行反射,都是耗时间,所以这里设置了缓存,对已经创建过对象的保存在这里,在拼接数据库语句或者是执行阶段需要用到,注入到其中,就可以省下时间。

  第三个增加的是异常类,不过我封装的比较简单,里面就一个可重载的方法,这个是用来发生异常时候,用户能够自己设置发生错误之后应该做什么(比如保存到日志)而定的。

  最后一个新增的是释放资源,因为对数据库连接,数据库连接数目比较少,但是command的数目在一般项目可就不是这样。可能大家为了方便,所以使用的时候尽情的new这样,那在我的框架设置了一个集合,专门存放command的,在用完后能够释放资源。因为考虑到在事务执行时候不能够对comand进行释放,所以在释放时候还做了判断。把有事务的command放到在事务执行后释放。

 

  看完上边的功能,是不是觉得十分强大,因为这个框架理解和实现起来都不容易,所以笔者尽可能的让大家明白,知道我是怎么一步步完成的。

  现在进入正题,先看下简单的结构图,看上去比较简单,不是我不会绘图,我在完成其他项目时候,都有完整的文档和图,因为现在没有太多时间,而且用软件画实在太慢了,所以大家将就的看吧。

  上图就是我框架的结构图。箭头代表关联,从下到上,代表底层到用户使用的层次。框架是支持对数据库的扩展,上边三个部分写继承抽象类就是如此,因为这几个其实就是实际数据库会使用到,所以使用工厂模式,这样就能够扩展其他了。

  好了,看完图,就开始讲解代码。

  我第一步是从数据库拼接语句开始做的,因为这个虽然还不算底层,但是相对于其他可以独立,那么看下这一跪部分的类:

using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace Dal{    public interface IDbCode    {        ///         /// 数据库执行表、视图、存储过程等对象        ///         /// 名称        /// 
IDbCode From(object Object); /// /// 查询 /// /// 查询的字段 ///
IDbCode Select(string Fields = "*"); /// /// 删除 /// ///
IDbCode Delete(); /// /// 更新 /// /// 更新对象 /// 更新字段 ///
IDbCode Update(object model,string Fields = ""); /// /// 插入 /// /// 插入对象 /// 插入字段 ///
IDbCode Insert(object model,string Fields = ""); /// /// 与条件 /// /// 条件字符串 ///
IDbCode AndWhere(string Where); /// /// 与条件 /// /// 字段 /// 值 ///
IDbCode AndWhere(string Field,object Value); /// /// 与条件 /// /// 条件字段 /// 嵌套查询条件委托 ///
IDbCode AndWhere(string Field, Func
Select); ///
/// 与条件 /// ///
值的类型
///
条件字段 ///
值 ///
IDbCode AndWhere
(string Field,List
Values); ///
/// 或条件 /// ///
条件字符串 ///
IDbCode OrWhere(string Where); ///
/// 或条件 /// ///
条件字段 ///
值 ///
IDbCode OrWhere(string Field, object Value); ///
/// 或条件 /// ///
条件字段 ///
嵌套条件 ///
IDbCode OrWhere(string Field, Func
Select); ///
/// 或条件 /// ///
值类型
///
条件字段 ///
值 ///
IDbCode OrWhere
(string Field, List
Values); ///
/// Top 语句 /// ///
///
IDbCode Top(int topCount); ///
/// 排序从小到大 /// ///
排序字段 ///
IDbCode OrderByAsc(string Field); ///
/// 排序从大到小 /// ///
排序字段 ///
IDbCode OrderByDesc(string Field); ///
/// 多表查询时候必须加的条件 /// ///
在两张表中的相同字段 ///
IDbCode ForMulTable(string Fields); string ToString(); ///
/// 清空缓存 /// ///
IDbCode Clear(); IDbCode CreateCode(string sql); object Paras { get; } void Dispose(); }}

继承它的类:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Data;using System.Data.SqlClient;using System.Reflection;namespace Dal{    public class SQLCode :IDbCode    {        string Object;        StringBuilder ExcuteString = new StringBuilder();        List
paras; Dictionary
> pro; static string[] s = { "select", "delect", "update", "insert" }; public SQLCode() { paras = new List
(); } public SQLCode(Dictionary
> pro) { paras = new List
(); this.pro = pro; } public SQLCode(List
paras, Dictionary
> pro) { this.paras = paras; this.pro = pro; } public IDbCode From(object Object) { Type t = Object.GetType(); if(t.Name.ToLower().Equals("string")) { this.Object = Object.ToString(); }else { this.Object = t.Name; } return this; } public IDbCode Select(string Fields = "*") { if (this.Object.Length <= 0) return this; if (!Check(0)) return this; ExcuteString.AppendLine("select " + Fields +" from "+ this.Object); ExcuteString.AppendLine(" where 1 = 1 "); return this; } bool Check(int Type) { int flag = 0; string b = ExcuteString.ToString(); for (int i = 0; i < s.Length; i++) if(i!=Type) flag += b.Contains(s[i]) ? 1 : 0; return flag == 0; } public IDbCode Delete() { if (Object.Length <= 0) return this; if (!Check(1)) return this; ExcuteString.AppendLine("delete " + this.Object); ExcuteString.AppendLine(" where 1 = 1 "); return this; } public IDbCode Update(object model, string Fields = "") { if (this.Object.Length <= 0) return this; if (!Check(2)) return this; Type t = model.GetType(); if (t.Name != Object) return this; ExcuteString.AppendLine("update "+this.Object +" set "); List
p; if(pro.ContainsKey(t.Name)) { p = pro[t.Name]; }else { p = t.GetProperties().ToList(); pro.Add(t.Name, p); } string f = ""; if(Fields.Length==0) { p.ForEach(o => { f += o.Name + " = @" + o.Name; paras.Add(new SqlParameter(o.Name, o.GetValue(model, null))); }); }else { string[] a = Fields.Split(','); p.ForEach(o => { if (a.Contains(o.Name)) { f += o.Name + " = @" + o.Name + ","; paras.Add(new SqlParameter(o.Name, o.GetValue(model, null))); } }); } ExcuteString.AppendLine(f); ExcuteString.AppendLine("where 1 = 1"); return this; } public IDbCode Insert(object model, string Fields = "") { if (this.Object.Length <= 0) return this; if (!Check(3)) return this; Type t = model.GetType(); if (t.Name != Object) return this; ExcuteString.AppendLine("insert " + this.Object); List
p; if (pro.ContainsKey(t.Name)) { p = pro[t.Name]; } else { p = t.GetProperties().ToList(); pro.Add(t.Name, p); } string f = "( "; string f1 = "values( "; if (Fields.Length == 0) { p.ForEach(o => { f += o.Name+","; paras.Add(new SqlParameter(o.Name, o.GetValue(model, null))); f1 += "@" + o.Name + ","; }); } else { string[] a = Fields.Split(','); p.ForEach(o => { if (a.Contains(o.Name)) { f += o.Name + ","; paras.Add(new SqlParameter(o.Name, o.GetValue(model, null))); f1 += "@" + o.Name + ","; } }); } f = f.Remove(f.LastIndexOf(','), 1) + " ) "; f1 = f1.Remove(f1.LastIndexOf(','), 1) + " ) "; ExcuteString.AppendLine(f); ExcuteString.AppendLine(f1); return this; } public IDbCode AndWhere(string Where) { ExcuteString.AppendLine(" and " + Where); return this; } public IDbCode AndWhere(string Field, object Value) { ExcuteString.AppendLine(" and " + Field + " = @" + Field); paras.Add(new SqlParameter(Field, Value)); return this; } public IDbCode AndWhere(string Field, Func
Select) { ExcuteString.AppendLine(" and " + Field + " in " + Select(new SQLCode(this.paras,this.pro))); return this; } public IDbCode AndWhere
(string Field, List
Values) { string value = "("; Values.ForEach(o => { value += o + ","; }); ExcuteString.AppendLine(" and " + Field + " in " + value.Remove(value.LastIndexOf(','), 1) + ")"); return this; } public IDbCode OrWhere(string Where) { ExcuteString.AppendLine(" or " + Where); return this; } public IDbCode OrWhere(string Field, object Value) { ExcuteString.AppendLine(" or " + Field + " = @" + Field); paras.Add(new SqlParameter(Field, Value)); return this; } public IDbCode OrWhere(string Field, Func
Select) { ExcuteString.AppendLine(" or " + Field + " in " + Select(new SQLCode(this.paras,this.pro))); return this; } public IDbCode OrWhere
(string Field, List
Values) { string value = "("; Values.ForEach(o => { value += o + ","; }); ExcuteString.AppendLine(" or " + Field + " in " + value.Remove(value.LastIndexOf(','), 1) + ")"); return this; } public IDbCode Top(int topCount) { if (!ExcuteString.ToString().Contains(s[0])) return this; ExcuteString.Replace("select", "select top " + topCount +" "); return this; } bool CheckHasOrderBy() { return this.ExcuteString.ToString().Contains("order by"); } public IDbCode OrderByAsc(string Field) { if (CheckHasOrderBy()) ExcuteString.AppendLine("," + Field + " asc"); else ExcuteString.AppendLine(" order by " + Field+" asc"); return this; } public IDbCode OrderByDesc(string Field) { if (CheckHasOrderBy()) ExcuteString.AppendLine("," + Field + " desc"); else ExcuteString.AppendLine(" order by " + Field + " desc"); return this; } public IDbCode ForMulTable(string Fields) { List
tables = this.Object.Split(',').ToList(); Fields.Split(',').ToList().ForEach(o => { for (int i = 0; i < tables.Count - 1; i++) { ExcuteString.AppendLine(" and " + tables[i] + "." + o + " = " + tables[i + 1] + "." + o); } }); return this; } public override string ToString() { return this.ExcuteString.ToString(); } public IDbCode Clear() { pro.Clear(); return this; } public IDbCode CreateCode(string sql) { ExcuteString.AppendLine(sql); return this; } public object Paras { get { return this.paras; } } public void Dispose() { this.pro = null; } }}

  如果有看过上次的文章,那么就知道这里部分方法用到反射,获取其中的属性来拼写语句。没什么难的,大家看到里面有许多的if条件,是我避免在链式组合时候,用户随便乱时候而设置的。这部分都是对字符串处理。

  第二部分是执行语句,这个相信大家写多了,先给代码:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Data;using System.Data.SqlClient;namespace Dal{    public interface IDbExcute    {        T ToModel
(IDbCode code, CommandType type = CommandType.Text) where T : class,new(); List
ToList
(IDbCode code,CommandType type = CommandType.Text) where T : class,new(); object ToResult(IDbCode code, CommandType type = CommandType.Text); int ExcuteResult(IDbCode code, CommandType type = CommandType.Text); DataTable ToDataTable(IDbCode code, CommandType type = CommandType.Text); DataSet ToDataSet(IDbCode code, CommandType type = CommandType.Text); void OpenConnection(); void CloseConnection(); void Dispose(object tran); void BeginTransation(string Name); void Commit(); void RollBack(); }}

上面就是支持整个框架的执行方法

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Data;using System.Data.SqlClient;using System.Reflection;namespace Dal{    public class SQLExcute : IDbExcute    {        SqlConnection conn;        Dictionary
> pro; Dictionary
command; SqlTransaction tran = null; public SQLExcute(SqlConnection conn, Dictionary
> pro) { this.conn = conn; this.pro = pro; command = new Dictionary
(); } public List
ToList
(IDbCode code,CommandType type = CommandType.Text) where T:class,new() { List
list = new List
(); string name = DateTime.Now.ToString(); command.Add(name, new SqlCommand()); SqlCommand com = command[name]; com.Connection = conn; com.CommandText = code.ToString(); com.CommandType = type; setCommand(com, (List
)code.Paras); Type t = typeof(T); List
pros; if(pro.ContainsKey(t.Name)) { pros = pro[t.Name]; }else { pros = t.GetProperties().ToList(); pro.Add(t.Name, pros); } try { this.OpenConnection(); using (SqlDataReader reader = com.ExecuteReader()) { while(reader.Read()) { T model = new T(); pros.ForEach(o => { if(ReaderExists(reader,o.Name)) { o.SetValue(model, reader[o.Name], null); } }); list.Add(model); } } } catch (Exception ex) { throw ex; } finally { this.Dispose(name); this.CloseConnection(); } return list; } public bool ReaderExists(SqlDataReader reader, string columnName) { //reader.GetSchemaTable().DefaultView.RowFilter = "ColumnName= '" + columnName + "'"; //return (reader.GetSchemaTable().DefaultView.Count > 0); return reader.GetSchemaTable().Select("ColumnName='" + columnName + "'").Length > 0; } public void Dispose(string name) { if(command.ContainsKey(name)) { SqlCommand com = command[name]; command.Remove(name); com.Dispose(); } if (command.Count <= 0) this.CloseConnection(); } public void Dispose(object tran) { List
list = command.Keys.ToList(); list.ForEach(o => { if(command[o].Transaction!=null&&command[o].Transaction==(SqlTransaction)tran) { this.Dispose(o); } }); } public object ToResult(IDbCode code, CommandType type = CommandType.Text) { string name = DateTime.Now.ToString(); command.Add(name, new SqlCommand()); SqlCommand com = command[name]; com.Connection = conn; com.CommandText = code.ToString(); com.CommandType = type; setCommand(com, (List
)code.Paras); object result =null; try { this.OpenConnection(); result = com.ExecuteScalar(); } catch (Exception ex) { DoException(); throw ex; }finally { this.Dispose(name); this.CloseConnection(); } return result; } private void DoException() { new DbException().Done(); } public int ExcuteResult(IDbCode code, CommandType type = CommandType.Text) { string name = DateTime.Now.ToString(); command.Add(name, new SqlCommand()); SqlCommand com = command[name]; com.Connection = conn; com.CommandText = code.ToString(); com.CommandType = type; setCommand(com, (List
)code.Paras); int result = 0; try { this.OpenConnection(); if (tran != null) com.Transaction = (SqlTransaction)tran; result = com.ExecuteNonQuery(); } catch (Exception ex) { DoException(); throw ex; } finally { if (tran == null) Dispose(name); this.CloseConnection(); } return result; } public System.Data.DataTable ToDataTable(IDbCode code, CommandType type = CommandType.Text) { string name = DateTime.Now.ToString(); command.Add(name, new SqlCommand()); SqlCommand com = command[name]; com.Connection = conn; com.CommandText = code.ToString(); com.CommandType = type; setCommand(com, (List
)code.Paras); DataTable dt = new DataTable(); try { using(SqlDataAdapter adapter = new SqlDataAdapter(com)) { adapter.Fill(dt); } } catch (Exception ex) { DoException(); throw ex; }finally { Dispose(name); } return dt; } public void setCommand(SqlCommand com,List
paras) { paras.ForEach(o => { com.Parameters.Add(o); }); } public System.Data.DataSet ToDataSet(IDbCode code, CommandType type = CommandType.Text) { string name = DateTime.Now.ToString(); command.Add(name, new SqlCommand()); SqlCommand com = command[name]; com.Connection = conn; com.CommandText = code.ToString(); com.CommandType = type; setCommand(com, (List
)code.Paras); DataSet dt = new DataSet(); try { using (SqlDataAdapter adapter = new SqlDataAdapter(com)) { adapter.Fill(dt); } } catch (Exception ex) { DoException(); throw ex; } finally { Dispose(name); } return dt; } public T ToModel
(IDbCode code, CommandType type = CommandType.Text) where T : class,new() { string name = DateTime.Now.ToString(); command.Add(name, new SqlCommand()); SqlCommand com = command[name]; com.Connection = conn; com.CommandText = code.ToString(); com.CommandType = type; setCommand(com, (List
)code.Paras); Type t = typeof(T); List
p = null; if(pro.ContainsKey(t.Name)) { p = pro[t.Name]; }else { p = t.GetProperties().ToList(); pro.Add(t.Name, p); } T model = new T(); try { this.OpenConnection(); using(SqlDataReader reader = com.ExecuteReader()) { if(reader.Read()) { p.ForEach(o => { if(ReaderExists(reader,o.Name)) { o.SetValue(model, reader[o.Name], null); } }); } } } catch (Exception ex) { DoException(); throw ex; } finally { Dispose(name); this.CloseConnection(); } return model; } public void OpenConnection() { if (this.conn.State != ConnectionState.Open) this.conn.Open(); } public void CloseConnection() { command.Values.ToList().ForEach(o => { if (o.Transaction != null) return; }); if (this.conn.State != ConnectionState.Closed) this.conn.Close(); } public void BeginTransation(string Name) { tran = conn.BeginTransaction(Name); } public void Commit() { tran.Commit(); Dispose(tran); tran = null; } public void RollBack() { tran.Rollback(); Dispose(tran); tran = null; } }}

  具体类中,主要设计了最重要的执行方法外,还像刚开始所述,设置了List,就是用来存放command对象,在执行完成时候时候它会自己释放。跟这个类配合使用的是异常类:

using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace Dal{    public class DbException    {        public virtual void Done()        {        }    }}

只有短短几句话,这就是用于使用者想发生异常想它干什么而设置的。

其实封装到这里,整个框架的支持已经形成,但是用户不能直接操作底层,而且还需要怎么对数据库进行实例,所以还要对上进行封装,下一步,往上走,数据库实例部分。

using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace Dal{    public interface IDbInstance    {        ///         /// 数据库名称        ///         string Name        {            get;        }        ///         /// 获取执行语句类        ///         IDbExcute Excute        {            get;        }        ///         /// 获取连接字符串        ///         string ConnectionString        {            get;        }        ///         /// 开启事务        ///         /// 事务名称        /// 
object getTransation(string TranName); /// /// 获取拼写字符串类 /// IDbCode Code { get; } }}

实现它的具体类包含所有的底层的操作,其实就是可以说是一个数据库实例了,创建一个就相当于一个数据库。里面将上边封装的类都在这里使用。

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Data;using System.Data.SqlClient;using System.Reflection;namespace Dal{    public class SQLInstance :IDbInstance    {        private SqlConnection conn;        private IDbExcute excute;        Dictionary
> pro; private string name; private string connectionString; private SqlTransaction tran = null; public SQLInstance(string Name,Dictionary
> pro, string ConnectionString) { this.name = Name; this.connectionString = ConnectionString; conn = new SqlConnection(ConnectionString); this.pro = pro; excute = new SQLExcute(conn,pro); } public string Name { get { return this.name; } } public IDbExcute Excute { get { return this.excute; } } public string ConnectionString { get { return this.connectionString; } } public object getTransation(string TranName) { return this.conn.BeginTransaction(TranName); } public IDbCode Code { get { return new SQLCode(pro); } } }}

接下来是控制连接的类:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Reflection;namespace Dal{    public class DbControl    {        //数据库服务        private static Dictionary
Server = new Dictionary
(); //存放缓存 private static Dictionary
> pro = new Dictionary
>(); private static DbControl control = new DbControl(); public static DbControl getInstance() { return control; } private DbControl() { } public IDbInstance createInstance(string Name,string ConnectionString,string type) { string nspace = typeof(IDbInstance).Namespace; Type t = Type.GetType(nspace + "." + type); object obj = Activator.CreateInstance(t, new object[] { Name, pro, ConnectionString }); IDbInstance instance = obj as IDbInstance; Server.Add(Name, instance); return instance; } public IDbInstance this[string Name] { get { if (Server.ContainsKey(Name)) return Server[Name]; else return null; } } }}

这里算是顶层的类,最主要就是存放数据库和缓存对象。也许会好奇如果存放数据库,还可以理解,但是放着模型对象的缓存,这是为什么?因为在字符连接,还有具体执行数据库语句时候都会使用到。而且,不仅这个数据库,别的数据库也会使用到,虽然在一个大项目可能用多个数据库,但是他们使用到项目里的模型是一致的吧,因此,将缓存设置在这里,最好不过。而且整个框架只有这一份,其他地方注入使用,就不会耗内存了。

最后就是用户使用的部分,这部分已经屏蔽掉许多底层的部分,只留下通用方法。这些都已经封装好了,直接在这里调用就可以。

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Data;namespace Dal{    public class DbHelper    {        private IDbInstance instance;        private string Name;        private DbControl control;        private IDbCode Code;        public DbHelper()        {            control = DbControl.getInstance();        }        public DbHelper createConnection(string Name, string ConnectionString, string type)        {            this.Name = Name;            instance = control.createInstance(Name, ConnectionString, type);            return this;        }        public DbHelper ExcuteString(Func
Fun) { Code = Fun(this.instance.Code); return this; } public DbHelper createTransation(string Name) { this.instance.Excute.BeginTransation(Name); return this; } public DbHelper Rollback() { this.instance.Excute.RollBack(); return this; } public DbHelper Commit() { this.instance.Excute.Commit(); return this; } public T ToModel
(CommandType Type = CommandType.Text) where T:class,new() { if (this.Code == null) return null; return this.instance.Excute.ToModel
(this.Code,Type); } List
ToList
(CommandType Type = CommandType.Text) where T:class,new() { if (this.Code == null) return null; return this.instance.Excute.ToList
(this.Code, Type); } object ToResult(CommandType Type = CommandType.Text) { if (this.Code == null) return null; return this.instance.Excute.ToResult(this.Code, Type); } int ExcuteResult(CommandType Type = CommandType.Text) { if (this.Code == null) return -1; return this.instance.Excute.ExcuteResult(this.Code, Type); } DataTable ToDataTable(CommandType Type = CommandType.Text) { if (this.Code == null) return null; return this.instance.Excute.ToDataTable(this.Code, Type); } DataSet ToDataSet(CommandType Type = CommandType.Text) { if (this.Code == null) return null; return this.instance.Excute.ToDataSet(this.Code, Type); } }}

 

  结束:快要熄灯了,没办法在写文章。如果大家对框架有什么不明白,可以在下面评论区问我。这就是更新之后的框架,我觉得还是蛮好用的,虽然还没经过严密的检测。有什么问题,大家也可以指导下,我也在学习中~

 

转载于:https://www.cnblogs.com/jnxzk/p/6493054.html

你可能感兴趣的文章
JAVA 技术类分享(二)
查看>>
android客户端向服务器发送请求中文乱码的问
查看>>
UOJ#220. 【NOI2016】网格 Tarjan
查看>>
Symfony翻译教程已开课
查看>>
Python模块之pickle(列表,字典等复杂数据类型与二进制文件的转化)
查看>>
通过数据库表反向生成pojo类
查看>>
css_去掉默认样式
查看>>
TensorFlow2.0矩阵与向量的加减乘
查看>>
NOIP 2010题解
查看>>
javascript中的each遍历
查看>>
String中各方法多数情况下返回新的String对象
查看>>
浅谈tcp粘包问题
查看>>
UVA11524构造系数数组+高斯消元解异或方程组
查看>>
排序系列之——冒泡排序、插入排序、选择排序
查看>>
爬虫基础
查看>>
jquery.lazyload延迟加载图片第一屏问题
查看>>
HDU 1011 Starship Troopers (树形DP)
查看>>
手把手教你写DI_1_DI框架有什么?
查看>>
.net常见的一些面试题
查看>>
OGRE 源码编译方法
查看>>