解剖实体框架(3)--与数据库交互

       实体框架一个基本的功能就是要与数据库交互,与数据库交互当然可以利用现有的一些基本技术,比如dotnet的ADO.Net等。这里有几个要点:

       1)要封装底层的数据库交互通道,这个可以利用现有的数据库访问技术,比如ADO.Net。

       2)构造与数据库交互的SQL语句,比如新增,删除,修改,查询等.做得比较好的,需要支持关联查询等。当然能做到AEF中的ESQL那样,还是有点难度。

       下面是一个实例,这里我只构造插入语句,并实现现数据库插入实体的功能示意(底层数据库访问封装这里不讲,这个有很多例子)。
       为了简单起见,我们假设实体类的类名就是数据库里对应表的表名,实体的属性对应数据库表里的字段,下面就是代码:

        //实体类。
        public class AModel
        {
            public string Field1{get;set;}
            public string Field2 { get; set; }
            public int age { get; set; }
            public string Name { get; set; }
        }
        /// <summary>
        /// SQL语句执行,可以采用普通类,也可以采用抽象接口,便于不同数据库的访问.
        /// </summary>
        public static  class DatabaseHelper
        {
            public static void ExecuteSQL(string strSQL, SqlParameter[] Params)
            {
                ;//......................
            }
        }
        /// <summary>
        /// 实体数据库基本操作,这里只是模拟实体插入数据库的情形,其它的操作其实类似,更新的话可以采用关键字做条件,冲突检查字段也可包含在条件里面.
        /// </summary>
        public static class DbOperation
        {
            public static void InsertModel<T>(T Model)
            {

                //这里的sql语句及参数构造结果可以采取缓存方式,没必要每次都去构造,以减少性能损失。
                Dictionary<string,string> thePP = new Dictionary<string,string>();
                Dictionary<string, SqlDbType> thePT = new Dictionary<string, SqlDbType>();
                string theSQL = SQLBuilderHelper.BuilderInsertSQL<T>(thePP,thePT);
                List<SqlParameter> theParams = new List<SqlParameter>();
                foreach (var item in thePP)
                {
                    SqlParameter theP = new SqlParameter();
                    theP.SqlDbType = thePT[item.Key];
                    theP.ParameterName = item.Value;
                    theP.Value = GetPropertyValue(Model, item.Key);
                    theParams.Add(theP);
                }
                DatabaseHelper.ExecuteSQL(theSQL.ToString(), theParams.ToArray());

            }

            //根据属性名获取属性值,可以单独放在一个辅助类里。
            private static object GetPropertyValue<T>(T Model, string PropertyName)
            {
                Type theType = Model.GetType();
                PropertyInfo pi = theType.GetProperty(PropertyName);
                return pi.GetValue(Model, null);
            }
        }
        /// <summary>
        /// SQL及参数构建器,这里构造sql语句的时候,在这里,默认类名就是数据库表名,属性名就是数据库字段名。但在实际构造这种框架的时候,可以采用元属性或者配置文件来实现这个功能。比如linqtosql,DbContext就采用元属性,AEF,Hibernate就是采用xml格式的配置文件,但这些东西都是为了更好更准确的构造sql语句.构造这种

        ///sql语  句    需要知道的包括表名,字段名,字段类型,语言类型与字段类型间的对照等。
        /// </summary>
        public class SQLBuilderHelper
        {
            public static string BuilderInsertSQL<T>(Dictionary<string, string> PropertyAndParams,Dictionary<string,SqlDbType> thePAndDBType)
            {
                Type theType = typeof(T);
                PropertyInfo[] theProperties = theType.GetProperties();
                StringBuilder theSQL1 = new StringBuilder();
                StringBuilder theSQL2 = new StringBuilder();
                PropertyAndParams.Clear();
                //其实这里也可以利用元属性来觉得哪些需要属性需要插入,哪些属性只能读取之类的判断,以及更新模式(对数字类型是赋值更新还是增量更新)等。
                foreach (var p in theProperties)
                {
                    if (theSQL1.ToString() == "")
                    {
                        theSQL1.Append(p.Name.ToUpper());
                        theSQL2.Append("@" + p.Name.ToUpper());
                    }
                    else
                    {
                        theSQL1.Append(","+p.Name.ToUpper());
                        theSQL2.Append(",@" + p.Name.ToUpper());
                    }
                    PropertyAndParams.Add(p.Name, "@" + p.Name.ToUpper());
                    thePAndDBType.Add(p.Name, GetDbType(p.PropertyType));

                }
                return "INSERT INTO " + theType.Name.ToUpper() + "(" + theSQL1.ToString() + ") VALUES(" + theSQL2.ToString() + ")";
            }

            //C#类型与数据库类型的对照可以通过Map文件完成,也可以通过元属性完成,个人觉得元属性的利用比较好,虽然说元属性不是很灵活,

            //但减少了构造的文件依赖,        在大型的项目中其实是有好处的,至少是便于管理的。
            private static SqlDbType GetDbType(Type type)
            {
                if (type == typeof(int))
                {
                    return SqlDbType.Int;
                }
                return SqlDbType.NVarChar;
            }
        }
       有的时候我们将实体对象称之为概念模型,而对象在数据库里的存储结构称之为物理模型,在这两个模型之间存在着一种映射关系(概念名称与物理名称的映射,对象属性与字段名之间的映射,以及对象之间关系的映射),这其实就是所谓的ORM(对象关系模型)。上面的例子这种映射是利用命名规范来完成,实际上做的时候可以采用配置文件或者元属性来完成,对于大项目时,配置文件的管理本身也会成为一个问题,因此我觉得用元属性数据来进行会比较好。

      上面的实现只是一个简单的示例,实际做的时候需考虑得东西会比较多,比如需要封装数据库差异(底层访问差异,sql语句方言),实体与数据库之间的对照(map),SQL语句及参数缓存问题(没必要每次都构造)等,但不管怎么说,基本的原理还是一样的,关键技术主要是反射机制的应用。

      下一篇,将介绍实体管理的功能,比如缓存,加载,新增,删除等....

你可能感兴趣的:(sql,数据库,Hibernate,框架,String,Class)