ORM框架学习
1.ORM框架简介
2.ORM框架实现
代码目录结构如上图所示,其中Theme.ORM是一个控制台窗体程序,Theme.Model是用来存放实体类的类库,Theme.Framework主要用来存放Mapping映射,Theme.DAL主要用来操作ADO.NET
Theme.ORM是程序的入口点,主要代码结构如下图所示
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Theme.DAL; using Theme.Model; namespace Theme.ORM { class Program { static void Main(string[] args) { SqlHelper help = new SqlHelper(); UserModel u1 = help.Query<UserModel>(1); Company com1 = help.Query<Company>(2); } } }
主要包含了查询UserModel实体和 Company 实体。
SqlHelper是数据库底层操作类,主要代码结构如下图所示
using System; using System.Collections.Generic; using System.Data.SqlClient; using System.Linq; using System.Reflection; using System.Text; using Theme.Framework; using Theme.Model; namespace Theme.DAL { public class SqlHelper { public T Query<T>(int id) where T : BaseModel { Type t=typeof(T); string conStr = "Server=127.0.0.1;Database=master; integrated security=SSPI"; string table = t.GetMappingName(); string props = string.Join(",", t.GetProperties().Select(p => "[" + p.GetMappingName() + "]")); T ti=Activator.CreateInstance<T>(); string sql = string.Format("select {0} from [{1}] where id='{2}'",props,table,id); try { using (SqlConnection con = new SqlConnection(conStr)) { SqlCommand cmd = new SqlCommand(sql, con); con.Open(); SqlDataReader reader = cmd.ExecuteReader(); while (reader.Read()) { foreach (var prop in t.GetProperties()) { var pro = prop.GetMappingName(); prop.SetValue(ti, reader[pro] is DBNull ? null : reader[pro], null); } } } } catch (Exception ex) { return null; } return ti; } } }
主要包含了一个泛型的查询方法Query(),这里使用泛型方法主要是为了支持不同的表结构,提高·代码利用率,减少代码冗余。该方法主要做以下几点说明:
1.通过where T : BaseModel约束调用该泛型方法的类必须继承自BaseModel,这样做的好处是限制执行查询方法的类型必须是数据库对应的实体类
2.通过应用反射实现动态拼接我们需要查询的字符串
3.通过特性来实现实体表名的名称变更(和数据库中的表名或字段名不一致)
4.注意DBNull和null的区别,赋值时注意转换下
5.GetMappingName()是Theme.Framework里面的方法,主要作用是提字段和表名变更的特性处理。
要实现自定义特性标记,必须创建自己的特性类,特性类都是继承自Attribute,我们创建了两个特性类ThemeColumn和ThemeTableAttribute,为了泛型化方法,这两个类都继承自一个ThemeAttribute,同时ThemeAttribute继承自Attribute。
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Theme.Framework { public abstract class ThemeAttribute:Attribute { public abstract string GetName(); } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Theme.Framework { public class ThemeColumn:ThemeAttribute { private String _RealName; public ThemeColumn(string name) { this._RealName = name; } public override string GetName() { return this._RealName; } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Theme.Framework { public class ThemeTableAttribute:ThemeAttribute { private string _RealName; public ThemeTableAttribute(string name) { this._RealName = name; } public override string GetName() { return _RealName; } } }
这样一来,我们就可以在做特性映射时,把特性映射方法泛型化出来
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; namespace Theme.Framework { public static class Mapping { public static string GetMappingName<T>(this T type) where T:MemberInfo { if (type.IsDefined(typeof(ThemeAttribute), true)) { ThemeAttribute t = type.GetCustomAttribute<ThemeAttribute>(); return t.GetName(); } else { return type.Name; } } //public static string GetTableName(this Type t) //{ // if (t.IsDefined(typeof(ThemeTableAttribute), true)) // { // ThemeTableAttribute tab = t.GetCustomAttribute<ThemeTableAttribute>(); // string name = tab.GetName(); // return name; // } // else // { // return t.Name; // } //} //public static string GetColumnName(this PropertyInfo prop) //{ // if (prop.IsDefined(typeof(ThemeColumn), true)) // { // ThemeColumn tab = prop.GetCustomAttribute<ThemeColumn>(); // string name = tab.GetName(); // return name; // } // else // { // return prop.Name; // } //} } }
GetMappingName方法就实现了下面注释的两个方法的功能,同时,为了能使用IsDefined这个方法,我们必须对泛型方法的类型做一些限制。我们查看Type继承的类和PropertyInfo继承的类,有一个公共的MemberInfo,我们就限制where T:MemberInfo。
ThemeAttribute类是ThemeTableAttribute和ThemeColumn的抽象类,这样就方便了我们的泛型方法的生成。