집 >백엔드 개발 >C#.Net 튜토리얼 >C# .NET의 더욱 스마트한 데이터베이스 작업 캡슐화에 대한 자세한 설명
전술:
데이터베이스 작업의 캡슐화는 다음과 같습니다. 인터넷에서 사용 가능 이미 데이터베이스 작업을 매우 잘 지원하는 ORM 프레임워크 또는 .NET 자체 EF가 많이 있습니다. 이 기사는 데이터베이스 작업의 간단한 캡슐화에 대한 내 생각을 공유하기 위한 것입니다. 이 기사에서는 독자의 초점이 데이터베이스 작업 캡슐화를 분석하고 설계하는 방법이며 코드는 부차적이라고 생각합니다. 게다가 이 글은 내 첫 번째 글이다. 구현 방법을 알아내는 데 며칠이 걸렸다. 코드는 블로그가 게시될 때 작성되었다. 그래서 사용상 버그가 있을 수 있다고 생각하고, try catch 예외 디자인은 없습니다.
이 프레임워크는 데이터베이스 독립적이어야 하며 어떤 데이터베이스이든지 사용할 수 있습니다. 그러나 초점은 코드가 아닌 분석에 있습니다. 따라서 더 잘 설명하기 위해 SQL Server만 캡슐화했습니다. 다른 측면에서는 프레임워크가 체인 작성을 지원할 수 있지만 많은 프로그래밍 언어에서는 체인 작성에 큰 문제가 있을 것입니다. 낯설기 때문에 데이터베이스 접근도 체인 모드로 만들 수 있을 것 같아요. 이 프레임워크에서는 SQL 문 작성이 필요하지 않으며, 필요한 매개변수를 전달하고 해당 작업을 캡슐화하기만 하면 됩니다.
글을 읽기 전에 제네릭, 리플렉션, 링크에 대한 기본적인 지식을 갖고 있는 것이 가장 좋으며, 그렇지 않으면 읽기가 다소 어려울 수 있습니다.
요점:
구조 프레임은 상대적으로 간단하고 간단한 팩토리 패턴을 사용하므로 저자는 UML 다이어그램을 그려 설명하지 않고 텍스트를 사용하여 내부 메소드를 설명합니다.
팩토리 인터페이스를 설계할 때 인터페이스에는 체인 작성에 필요한 세 단계(부분이라고도 함)가 포함되어야 한다는 점을 고려해야 합니다. 기본 데이터베이스 작업(오픈) , 폐쇄, 생성 등), 데이터베이스 추가, 삭제, 수정, 데이터베이스에서 반환하는 데이터(여기서는 실행 단계라고 생각하는데, 왜 이전 단계가 아닌지 궁금하실 텐데요. 아래 참조) 및 필수적이지 않은 거래 작업.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data; using System.Data.SqlClient; namespace Dal { public interface DbHelper { /// <summary> /// 创建数据库连接 /// </summary> /// <param name="connectionString">连接字符串</param> /// <returns></returns> DbHelper createConnection(string connectionString); /// <summary> /// 打开数据库 /// </summary> /// <returns></returns> DbHelper openConnection(); /// <summary> /// 关闭数据库 /// </summary> /// <returns></returns> DbHelper closeConnection(); /// <summary> /// 释放sqlConnection对象 /// </summary> void DisposedConnection(); /// <summary> /// 释放sqlCommand对象 /// </summary> void DisposedCommand(); /// <summary> /// 创建sqlCommand对象 /// </summary> /// <returns></returns> DbHelper createCommand(); /// <summary> /// 设置sqlCommand的类型 /// </summary> /// <param name="type">CommandType枚举类型</param> /// <returns></returns> DbHelper setCommandType(CommandType type); /// <summary> /// 要查询的表(多表以逗号隔开)、存储过程、视图名 /// </summary> /// <param name="Name"></param> /// <returns></returns> DbHelper FromName(string Name); /// <summary> /// 创建事务 /// </summary> /// <returns></returns> DbHelper beginTransaction(); /// <summary> /// 事务回滚 /// </summary> /// <returns></returns> DbHelper TransactionRowBack(); /// <summary> /// 事务提交 /// </summary> /// <returns></returns> DbHelper TransactionCommit(); /// <summary> /// 对多张表间的列进行联系 /// </summary> /// <param name="Fields">表间联系的字段</param> /// <returns></returns> DbHelper ForMulTable(string Fields); /// <summary> /// 查询 /// </summary> /// <param name="Fields">查询字段</param> /// <param name="Where">查询条件字典</param> /// <param name="otherWhere">其他条件</param> /// <returns></returns> DbHelper Select(string Fields = "*", Dictionary<string, object> Where = null, string otherWhere = ""); /// <summary> /// 更新 /// </summary> /// <param name="model">需要更新的对象</param> /// <param name="Where">更新条件</param> /// <param name="Fields">更新字段</param> /// <param name="otherWhere">其他条件</param> /// <returns></returns> DbHelper Update(object model, Dictionary<string, object> Where, string Fields = "", string otherWhere = ""); /// <summary> /// 插入 /// </summary> /// <param name="model">需要插入的对象</param> /// <param name="Fields">需要插入的字段</param> /// <returns></returns> DbHelper Insert(object model, string Fields = ""); /// <summary> /// 删除 /// </summary> /// <param name="Where">删除条件</param> /// <param name="otherWhere">其他条件</param> /// <returns></returns> DbHelper Delete(Dictionary<string, object> Where, string otherWhere = ""); /// <summary> /// 查询返回List /// </summary> /// <typeparam name="T">模型</typeparam> /// <returns></returns> List<T> ToList<T>() where T : class ,new(); /// <summary> /// 查询返回DataSet /// </summary> /// <param name="DatasetName"></param> /// <returns></returns> DataSet ToDataSet(string DatasetName); /// <summary> /// 查询返回DataTable /// </summary> /// <returns></returns> DataTable ToDataTable(); /// <summary> /// 执行存储过程 /// </summary> /// <param name="Parameter">存储过程参数</param> /// <returns></returns> DbHelper ExcuteProc(Dictionary<string, object> Parameter); /// <summary> /// 执行返回查询第一行第一列值 /// </summary> /// <returns></returns> object Result(); /// <summary> /// 返回执行的影响行数 /// </summary> /// <returns></returns> int ExcuteResult(); /// <summary> /// 用户自定义sqlCommand /// </summary> /// <param name="fun">委托</param> void UserDefineOperation(Action<dynamic> fun); } }
좋습니다. 코드를 읽은 후에도 여전히 구체적인 구현에 대해 모두가 혼란스러울 것입니다. 그런 다음 SQL Server를 사용하여 단계별로 구체적인 구현을 분석해 보겠습니다. 분석하다.
SQLHelper의 특정 구현 클래스에서는 설계에 꼭 필요한 필드이다. 설계 초기에는 각 데이터베이스가 사용하는 실행 객체 Command가 다르기 때문에 어떻게 하면 데이터베이스를 호환되게 만들 수 있을까 고민하다가 라이브러리를 더 잘 캡슐화하기 위해 sqlCommand는 외부에 노출되지 않도록 설계했지만, 내부적으로 사용됩니다. 노출된 메소드는 com의 속성 을 설정할 수 있으며, ExcuteName 객체는 실행 데이터를 저장합니다.
//连接字符串 string ConnectionString; //数据库连接对象 private SqlConnection conn; //执行对象 SqlCommand com; //表、存储过程、视图名称 string ExcuteName; //事务 SqlTransaction tran; //sql语句 StringBuilder sqlBuilderString; //参数 SqlParameter[] paras;
1부: 기본 데이터베이스 작업
createConnection 메소드: 이 메소드는 실제로 ConnectionString을 할당하는 새로운 sqlConnection은 일반적으로 사용되는 단일 케이스 모드를 채택하여 실행 중에 더욱 안전합니다. 그런데 이 싱글톤은 정적으로 설계한 것이 아니라 sqlConnection에 해당하는 Helper를 참조한 것인데, 일부 프로젝트에서는 여러 데이터베이스에 접근할 수 있을 것으로 생각되기 때문입니다. 그리고 생성할 때 한번 열었다 닫아 실제로 사용 가능한지 확인해보세요.
public DbHelper createConnection(string connectionString) { if (!ConnectionCanUse()) { this.ConnectionString = connectionString; conn = new SqlConnection(this.ConnectionString); } return this; } /// <summary> /// 检查conn是否能用 /// </summary> /// <returns></returns> public bool ConnectionCanUse() { if (conn == null) return false; try { conn.Open(); conn.Close(); }catch(Exception e) { return false; } return true; }
열기, 닫기, 연결 해제 및 명령 생성에 대한 설명은 한 문장만 있으므로 설명하지 않겠습니다.
기본 연산의 경우에도 sqlCommandType의 설정이 있는데, 저장 프로시저와 일반문 등의 연산 문자열이 확연히 다르기 때문에 메소드를 반드시 지정해야 한다. 설정하기 위해 작성되었습니다.
第二部分:增删改查的操作 这里就解释为什么sql语句不是在这个阶段执行。我觉得,如果将具体的执行放在这个阶段,那么就会导致方法重载过多,为什么?因为并不是所有人都能考虑到使用者要返回的类型,比如我想要List,或者DataSet等等,而且还会将这个方法的作用过重:在我设计的这些方法中,实操作的是对sql语句的生成,所以说为什么不能在这边执行,那么就不能重用。是吧,这样设计很灵活,将数据库真正执行放在下个阶段。而且这些方法都是链式的写法,所以会对执行能够很灵活的控制,最重要能够重用,不需要写别的重载方法,只需要一个方法。
生成sql语句在这也是简单的封装,如果要做起真的框架,我觉得sql字符串的组合还应该创建一个类,来更智能的组合用户的需求。
自然,里面使用到反射、Linq。不过笔者也一步步解释,将怎么设计分享给大家。
大家看到Select、Insert、Update、Delete的接口都有Where的条件字典。没错,反射就在这里使用。为了考虑到数据库的安全,所以sql自然只是简单的拼接,还应该使用参数。所以,反射就用在Where生成参数上。大家也许还看到别的otherWhere,这个怎么不设计成参数,因为Where能够实现的,其实就是赋值语句,也就是表内某字段 = 值,所以需要。在otherWhere中,存放的是其他特殊的条件。前面说这里设计的不完美,就因为如此,其实有些条件 like 或者 使用or ,虽然能够写在otherWhere中,但是没办法使用参数来控制。
那么接下来就是Fiels参数了,这个在各个方法充当不同的作用。Select是查询的字段,Update中是更新的字段,在Insert中是插入的字段,这样就灵活的控制了。在这些字段为空的时候,默认为全部,反射在这里就使用了,遍历模型对象中的属性,然后将它们一个个填进sql语句中。在这里比较注意的应该是插入,因为大家在写sql语句时候是这样的 insert tableName values(value,value....)这样的格式,这样是因为sql会自己对应值插入,而在程序中的模型类中,我想大家写属性可不是按顺序的吧,所以在反射遍历时候,就有可能将几个本来待在某个列位置的值去换了位置的情况。所以,这里在遍历的时候,应该按插入的完全格式来设计,也就是 insert tableName(Field,Field...) values(value,value...)。
在这几个方法中,Delete最简单。
public DbHelper Select(string Fields = "*",Dictionary<string,object> Where = null,string otherWhere = "") { sqlBuilderString = new StringBuilder(); sqlBuilderString.AppendLine("select " + Fields + " from " + this.ExcuteName); List<SqlParameter> paras = new List<SqlParameter>(); sqlBuilderString.AppendLine(" where 1 = 1 "); if (Where != null && Where.Count > 0) { paras = new List<SqlParameter>(); //遍历Where,将里面的条件添加到sqlParamter和sql语句中 Where.Keys.ToList().ForEach(o => { sqlBuilderString.AppendLine(" and "+ o + " = @" + o); paras.Add(new SqlParameter(o, Where[o])); }); } if(!string.IsNullOrEmpty(otherWhere)) { sqlBuilderString.AppendLine(otherWhere); } this.paras = paras.ToArray(); return this; }
public DbHelper Update(object model,Dictionary<string, object> Where,string Fields = "", string otherWhere = "") { Type t = model.GetType(); List<string> keys = Where.Keys.ToList(); sqlBuilderString = new StringBuilder(); bool firstnode = true; sqlBuilderString.AppendLine("update "+ExcuteName + " set "); List<SqlParameter> paras = new List<SqlParameter>(); if(string.IsNullOrEmpty(Fields)) { t.GetProperties().ToList().ForEach(o => { if (!firstnode) sqlBuilderString.Append(","); else firstnode = false; if(!keys.Contains(o.Name)) { sqlBuilderString.AppendLine(o.Name + " = @"+o.Name); paras.Add(new SqlParameter(o.Name,o.GetValue(model,null))); } }); }else { Fields.Split(',').ToList().ForEach(o => { sqlBuilderString.AppendLine(o + " = @" + o); paras.Add(new SqlParameter(o, t.GetProperty(o).GetValue(model, null))); }); } this.paras = paras.ToArray(); return this; }
public DbHelper Insert(object model,string Fields = "") { List<SqlParameter> paras = new List<SqlParameter>(); Type t = model.GetType(); sqlBuilderString = new StringBuilder(); sqlBuilderString.AppendLine("insert " + ExcuteName); if(string.IsNullOrEmpty(Fields)) { string s = ""; string s1=""; t.GetProperties().ToList().ForEach(o => { s += o.Name + ","; s1 += " @" + o.Name + ","; paras.Add(new SqlParameter(o.Name, o.GetValue(model, null))); }); s.Remove(s.LastIndexOf(','),1); s1.Remove(s.LastIndexOf(','), 1); sqlBuilderString.AppendLine("(" + s + ")"); sqlBuilderString.AppendLine(" values(" + s1 + ")"); }else { sqlBuilderString.AppendLine("(" + Fields + ")"); string s = ""; Fields.Split(',').ToList().ForEach(o => { s += " @" + o + ","; paras.Add(new SqlParameter(o, t.GetProperty(o).GetValue(model, null))); }); sqlBuilderString.AppendLine(" values(" + s + ")"); } this.paras = paras.ToArray(); return this; }
public DbHelper Delete(Dictionary<string,object> Where,string otherWhere = "") { sqlBuilderString = new StringBuilder(); List<SqlParameter> paras = new List<SqlParameter>(); sqlBuilderString.AppendLine("delete " + ExcuteName); sqlBuilderString.AppendLine(" where 1 = 1 "); Where.Keys.ToList().ForEach(o => { sqlBuilderString.AppendLine(" and " + o + " = @" + o); paras.Add(new SqlParameter(o, Where[o])); }); this.paras = paras.ToArray(); return this; }
最后一个阶段,那就是执行阶段,这里封装了些执行的方法。
这个也是简单,最重要的方法应该是setCommand,这个方法是对sqlCommand进行设置,执行的语句,以及添加参数。
private void setCommand() { if(com.CommandType== CommandType.StoredProcedure) { this.com.CommandText = ExcuteName; }else { this.com.CommandText = sqlBuilderString.ToString(); } this.paras.ToList().ForEach(o => { this.com.Parameters.Add(o); }); }
其他就是执行的语句。
public List<T> ToList<T>() where T:class ,new() { List<T> list = new List<T>(); setCommand(); SqlDataReader reader = com.ExecuteReader(); Type t = typeof(T); List<PropertyInfo> pros = t.GetProperties().ToList(); while(reader.Read()) { T model = new T(); pros.ForEach(o => { o.SetValue(model, reader[o.Name], null); }); list.Add(model); } reader.Dispose(); return list; } public DataSet ToDataSet(string DatasetName = "") { DataSet ds = new DataSet(); setCommand(); SqlDataAdapter adapter = new SqlDataAdapter(com); adapter.Fill(ds, string.IsNullOrEmpty(DatasetName) ? this.ExcuteName.Replace(",", "_") : DatasetName); adapter.Dispose(); return ds; } public DataTable ToDataTable() { DataTable dt = new DataTable(); setCommand(); SqlDataAdapter adapter = new SqlDataAdapter(com); adapter.Fill(dt); adapter.Dispose(); return dt; } public object Result() { setCommand(); return com.ExecuteScalar(); } public int ExcuteResult() { setCommand(); return com.ExecuteNonQuery(); } public DbHelper ExcuteProc(Dictionary<string,object> Parameter) { List<SqlParameter> paras = new List<SqlParameter>(); Parameter.Keys.ToList().ForEach(o => { paras.Add(new SqlParameter(o, Parameter[o])); }); return this; }
当然,还不能少了让用户自定义的方法,所以最后还留了个方法,参数是委托。委托里面的参数还是动态类型,这就懂了吧,想用户怎么用,你就怎么定义。
public void UserDefineOperation(Action<dynamic> fun) { fun(this.com); }
好了,设计也就到这里,下面就贴上SQLHelper完整的代码。
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 SQLHelper:DbHelper { //连接字符串 string ConnectionString; //数据库连接对象 private SqlConnection conn; //执行对象 SqlCommand com; //表、存储过程、视图名称 string ExcuteName; //事务 SqlTransaction tran; //sql语句 StringBuilder sqlBuilderString; //参数 SqlParameter[] paras; private SQLHelper() { } ////// 创建sqlHelper静态方法 /// ///public static DbHelper getInstance() { return new SQLHelper(); } /// /// /// /// ///public DbHelper createConnection(string connectionString) { if (!ConnectionCanUse()) { this.ConnectionString = connectionString; conn = new SqlConnection(this.ConnectionString); } return this; } /// /// 检查conn是否能用 /// ///public bool ConnectionCanUse() { if (conn == null) return false; try { conn.Open(); conn.Close(); }catch(Exception e) { return false; } return true; } /// /// /// ///public DbHelper openConnection() { if(conn.State != ConnectionState.Open) this.conn.Open(); return this; } /// /// /// ///public DbHelper closeConnection() { if(conn.State != ConnectionState.Closed) this.conn.Close(); return this; } /// /// /// public void DisposedConnection() { if (!ConnectionBeUsed()) this.conn.Dispose(); } ////// 检查数据库是否在被打开使用 /// ///public bool ConnectionBeUsed() { if(conn.State == ConnectionState.Open) return true; return false; } /// /// /// ///public DbHelper createCommand() { if (this.com == null) { this.com = new SqlCommand(); com.Connection = this.conn; } return this; } /// /// /// public void DisposedCommand() { this.com.Dispose(); } ////// /// /// ///public DbHelper setCommandType(CommandType type) { this.com.CommandType = type; return this; } /// /// /// /// ///public DbHelper FromName(string Name) { this.ExcuteName = Name; return this; } /// /// /// ///public DbHelper beginTransaction() { this.tran = conn.BeginTransaction(); com.Transaction = this.tran; return this; } /// /// /// ///public DbHelper TransactionRowBack() { if(tran!=null) { tran.Rollback(); } return this; } /// /// /// ///public DbHelper TransactionCommit() { if(tran!=null) { tran.Commit(); tran = null; } return this; } /// /// /// /// /// /// ///public DbHelper Select(string Fields = "*",Dictionary<string,object> Where = null,string otherWhere = "") { sqlBuilderString = new StringBuilder(); sqlBuilderString.AppendLine("select " + Fields + " from " + this.ExcuteName); List<SqlParameter> paras = new List<SqlParameter>(); sqlBuilderString.AppendLine(" where 1 = 1 "); if (Where != null && Where.Count > 0) { paras = new List<SqlParameter>(); //遍历Where,将里面的条件添加到sqlParamter和sql语句中 Where.Keys.ToList().ForEach(o => { sqlBuilderString.AppendLine(" and "+ o + " = @" + o); paras.Add(new SqlParameter(o, Where[o])); }); } if(!string.IsNullOrEmpty(otherWhere)) { sqlBuilderString.AppendLine(otherWhere); } this.paras = paras.ToArray(); return this; } public DbHelper ForMulTable(string Fields) { List tables = ExcuteName.Split(',').ToList(); Fields.Split(',').ToList().ForEach(o => { for(int i = 0;i 最后还有两个事务的方法,前面忘记说了,其实就是SqlTransaction,在里面的sqlCommand加上这个,就可以实现。或许有人会问,如果有同一时间段有好几个sqlCommand怎么办?不会的,sqlCommand我也设置成单例,就不会发生控制不了的事情了。
결론: 제가 처음으로 '어리석은 일'을 많이 해봤지만, 그래도 대학생 후배라서 부담스럽게 글을 쓰게 된다면 걱정이 됩니다. 조롱의 대상이 될 뿐이고, 유치한 '일'이 모두가 볼 수 있도록 온라인에 게시하기에는 너무 부끄럽습니다. 그래서 며칠 동안 고민한 끝에 꽤 유용하다고 생각되는 패키지를 작성했습니다. 비록 많은 프로젝트에서는 작동하지 않을 수도 있지만 독자들이 스스로 더 깊이 생각할 수 있을 것입니다.
이 프레임워크는 SQL 문 조합, 호출 시 예외 처리, 체인 조합을 더 잘 구현하는 방법 등을 더 잘 캡슐화해야 한다고 생각합니다. , 다중 데이터베이스 처리 제어, 잠금 추가 등도 가능하다고 생각합니다. 웹을 할 때 winform과 같지 않고 각 끝마다 자체 연결이 있습니다. 제가 생각하는 또 다른 좋은 점은 프레임워크가 기본 키와 외래 키를 식별하고 프로그램에서 SQL로 연결을 설정할 수 있도록 모델에 대한 처리를 수행하고 기능을 추가하는 것입니다. 그러면 독자들이 생각할 몫이다.
위 내용은 C# .NET의 더욱 스마트한 데이터베이스 작업 캡슐화에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!