Explication détaillée de l'encapsulation d'opérations de base de données plus intelligentes en C# .NET

迷茫
Libérer: 2017-03-26 14:36:01
original
1698 Les gens l'ont consulté

Ce qui précède :

L'encapsulation des opérations de base de données est considérée comme étant disponible sur Internet Il existe déjà de nombreux frameworks ORM, ou le propre EF de .NET, qui prennent tous très bien en charge les opérations de base de données. Cet article a pour but de partager mes réflexions sur une simple encapsulation des opérations de base de données. Pour cet article, je pense que les téléspectateurs se concentrent sur la façon d'analyser et de concevoir l'encapsulation des opérations de base de données, et le code vient en deuxième position. De plus, c'est mon premier article, il m'a fallu quelques jours pour comprendre comment l'implémenter. Le code a été écrit lors de la publication du blog. Je pense donc qu'il peut y avoir des bugs dans l'utilisation, et il n'y a pas de conception d'exception try catch.

Ce framework doit être indépendant de la base de données et peut être utilisé quelle que soit la base de données dont il s'agit. Cependant, l’accent est mis sur l’analyse et non sur le code. Par conséquent, afin de mieux expliquer, j'ai uniquement encapsulé SQL Server. En d'autres termes, les téléspectateurs peuvent le concevoir eux-mêmes ; le framework peut prendre en charge l'écriture en chaîne. Je pense que dans de nombreux langages de programmation, tout le monde a un gros problème avec l'écriture en chaîne. ne soit pas familier, donc je pense que l'accès à la base de données peut également être transformé en mode chaîne. Ce framework ne nécessite pas d'écriture d'instructions SQL. Pour toute opération, il vous suffit de transmettre les paramètres requis et d'encapsuler les opérations correspondantes.

Il est préférable d'avoir quelques connaissances de base sur les génériques, la réflexion et Link avant de lire l'article, sinon la lecture peut être un peu difficile.

Pour aller droit au but :

La structure de le cadre est relativement simple, utilisant le modèle d'usine simple, donc l'auteur ne dessine pas de diagramme UML pour expliquer, mais utilise du texte pour décrire les méthodes à l'intérieur.

Lors de la conception de l'interface d'usine, vous devez considérer que l'interface doit contenir les trois étapes (également appelées parties) nécessaires à l'écriture en chaîne : opérations de base de base de données (ouverture , Fermeture, création, etc.), ajouts, suppressions, modifications de la base de données, données renvoyées par la base de données (ici je la considère comme l'étape d'exécution, j'imagine que vous serez curieux de savoir pourquoi ce n'est pas l'étape précédente, vous le saurez comme vous lire ci-dessous) et les opérations de transaction non essentielles.

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);
    }
}
Copier après la connexion

D'accord, après avoir lu le code, tout le monde devrait encore être confus quant à l'implémentation spécifique. Ensuite, analysons l'implémentation spécifique étape par étape, comme. suit SQL Server pour analyser.

Dans la classe d'implémentation spécifique de SQLHelper, c'est un champ nécessaire dans la conception. Au début de la conception, je réfléchissais à la façon de rendre chaque base de données compatible, car l'objet d'exécution Command qu'elles utilisent est différent, donc afin de mieux encapsuler la bibliothèque, j'ai conçu le sqlCommand pour ne pas être exposé à un usage externe, mais à utiliser en interne. La méthode exposée peut définir l'attribut de com et l'objet ExcuteName stocke les données d'exécution.

//连接字符串
        string ConnectionString;
        
        //数据库连接对象
        private SqlConnection conn;

        //执行对象
        SqlCommand com;

        //表、存储过程、视图名称
        string ExcuteName;

        //事务
        SqlTransaction tran;

        //sql语句
        StringBuilder sqlBuilderString;

        //参数
        SqlParameter[] paras;
Copier après la connexion

Première partie : Opérations de base de base de données

méthode createConnection : cette méthode En fait, il s'agit du nouveau sqlConnection, et la connectionString qui lui est assignée adopte également le mode cas unique que tout le monde utilise généralement, qui sera également plus sûr lors de l'exécution. Cependant, ce singleton fait référence à un Helper correspondant à un sqlConnection, plutôt que d'être conçu comme static, car je pense que certains projets peuvent accéder à plusieurs bases de données. Et lors de sa création, ouvrez-le et fermez-le une fois pour vérifier s'il peut réellement être utilisé.

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;
        }
Copier après la connexion

Je n'expliquerai pas l'ouverture, la fermeture, la libération de la connexion et la création d'une commande, car il ne contient qu'une seule phrase.

Concernant les opérations de base, il y a aussi le réglage de sqlCommandType, car les chaînes d'opération telles que les procédures stockées et les instructions ordinaires sont évidemment différentes, donc une méthode doit être écrit pour le mettre en place.

  第二部分:增删改查的操作 这里就解释为什么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;
        }
Copier après la connexion
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(&#39;,&#39;).ToList().ForEach(o =>
                {
                    sqlBuilderString.AppendLine(o + " = @" + o);
                    paras.Add(new SqlParameter(o, t.GetProperty(o).GetValue(model, null)));
                });
            }            this.paras = paras.ToArray();            return this;
        }
Copier après la connexion
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(&#39;,&#39;),1);
                s1.Remove(s.LastIndexOf(&#39;,&#39;), 1);
                sqlBuilderString.AppendLine("(" + s + ")");
                sqlBuilderString.AppendLine(" values(" + s1 + ")");
            }else
            {
                sqlBuilderString.AppendLine("(" + Fields + ")");                string s = "";
                Fields.Split(&#39;,&#39;).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;
        }
Copier après la connexion
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;
        }
Copier après la connexion

最后一个阶段,那就是执行阶段,这里封装了些执行的方法。

这个也是简单,最重要的方法应该是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);
            });
        }
Copier après la connexion

其他就是执行的语句。

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;
        }
Copier après la connexion

当然,还不能少了让用户自定义的方法,所以最后还留了个方法,参数是委托。委托里面的参数还是动态类型,这就懂了吧,想用户怎么用,你就怎么定义。

public void UserDefineOperation(Action<dynamic> fun)
        {
            fun(this.com);
        }
Copier après la connexion

好了,设计也就到这里,下面就贴上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
Copier après la connexion

最后还有两个事务的方法,前面忘记说了,其实就是SqlTransaction,在里面的sqlCommand加上这个,就可以实现。或许有人会问,如果有同一时间段有好几个sqlCommand怎么办?不会的,sqlCommand我也设置成单例,就不会发生控制不了的事情了。

 

Conclusion : Ceci est mon premier blog Même si j'ai fait beaucoup de « travaux enfantins », après tout, je suis en première année à l'université. Si j'écris avec désinvolture, je suis inquiet. que je vais juste devenir une cible. Objet du ridicule, le « travail » enfantin est trop embarrassant pour être mis en ligne à la vue de tous. Ainsi, après y avoir réfléchi pendant quelques jours, j'ai écrit un package que je pense très utile. Même s'il ne fonctionne pas pour de nombreux projets, les lecteurs peuvent y réfléchir plus profondément eux-mêmes.

Je pense que ce cadre devrait être mieux encapsulé, comme la combinaison d'instructions SQL, la gestion des exceptions lors de l'appel, comment mieux réaliser la combinaison de chaînes , le contrôle du traitement multi-bases de données et l'ajout de verrous, je pense que c'est également possible. Après tout, quand on fait du web, ce n'est pas comme winform, chaque extrémité a sa propre connexion. Une autre chose que je trouve bonne est d'effectuer un traitement sur le modèle et d'ajouter des fonctionnalités afin que le framework puisse identifier les clés primaires et étrangères, établir des connexions en SQL, etc. dans le programme. Ensuite, c'est aux lecteurs de réfléchir.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!