NET 手写ORM
对象关系映射(英语:(Object Relational Mapping,简称ORM,或O/RM,或O/R mapping),是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换 。从效果上说,它其实是创建了一个可在编程语言里使用的--“虚拟对象数据库”。
面向对象是从软件工程基本原则(如耦合、聚合、封装)的基础上发展起来的,而关系数据库则是从数学理论发展而来的,两套理论存在显著的区别。为了解决这个不匹配的现象,对象关系映射技术应运而生。
对象关系映射(Object-Relational Mapping)提供了概念性的、易于理解的模型化数据的方法。ORM方法论基于三个核心原则: 简单:以最基本的形式建模数据。 传达性:数据库结构被任何人都能理解的语言文档化。 精确性:基于数据模型创建正确标准化的结构。 典型地,建模者通过收集来自那些熟悉应用程序但不熟练的数据建模者的人的信息开发信息模型。建模者必须能够用非技术企业专家可以理解的术语在概念层次上与数据结构进行通讯。建模者也必须能以简单的单元分析信息,对样本数据进行处理。ORM专门被设计为改进这种联系。
简单的说:ORM相当于中继数据。具体到产品上,例如ADO.NET Entity Framework。DLINQ中实体类的属性[Table]就算是一种中继数据。
BaseModel示例代码:
public class BaseModel { /// <summary> /// 所有实体的父类,非自增主键GUID /// </summary> public string Id { set; get; } }
User示例代码:
public class User : BaseModel { public string Account { get; set; } public string Password { get; set; } public string Name { get; set; } public int Sex { get; set; } public int Status { get; set; } public string BizCode { get; set; } public DateTime CreateTime { get; set; } public string CrateId { get; set; } public string TypeName { get; set; } public string TypeId { get; set; } }
IBaseDAL示例代码
public interface IBaseDAL { /// <summary> /// 根据主键返回一个实体 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="id"></param> /// <returns></returns> T Find<T>(string id) where T : BaseModel; /// <summary> /// 返回一个List<Medel>实体集合 /// </summary> /// <typeparam name="T"></typeparam> /// <returns></returns> List<T> FindAll<T>() where T : BaseModel; /// <summary> /// 添加一条记录 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="t"></param> /// <returns></returns> bool Add<T>(T t) where T : BaseModel; /// <summary> /// 更新一个实体 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="t"></param> /// <returns></returns> bool Update<T>(T t) where T : BaseModel; /// <summary> /// 删除一条记录 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="t"></param> /// <returns></returns> bool Delete<T>(T t) where T : BaseModel; }
BaseDAL示例代码
/// <summary> /// 泛型方法添加BaseModel约束,所有实体类必须有一个非自增的主键Id,Id的值为随机的GUID, /// </summary> public class BaseDAL : IBaseDAL { /// <summary> /// 数据库连接字符串 /// </summary> private static string ConnectionString = ConfigurationManager.ConnectionStrings["OpenAuthDB"].ConnectionString; public bool Add<T>(T t) where T : BaseModel { Type type = t.GetType(); string columnStr = string.Join(",", type.GetProperties(BindingFlags.Instance | BindingFlags.Public).Select(p => $"[{p.Name}]")); string valueStr = string.Join(",", type.GetProperties(BindingFlags.Instance | BindingFlags.Public).Select(p => $"@{p.Name}")); var parameterList = type.GetProperties(BindingFlags.Instance | BindingFlags.Public).Select(p => new SqlParameter($"@{p.Name}", p.GetValue(t) ?? DBNull.Value)); string sqlStr = $"Insert Into [{type.Name}] ({columnStr}) values ({valueStr})"; using (SqlConnection conn = new SqlConnection(ConnectionString)) { SqlCommand command = new SqlCommand(sqlStr, conn); command.Parameters.AddRange(parameterList.ToArray()); conn.Open(); //如果Id是自增的,在sql后面增加个 Select @@Identity; command.ExecuteScalar,新增后把Id拿出来. return command.ExecuteNonQuery() > 0; } } public bool Delete<T>(T t) where T : BaseModel { //获取T的类型 Type type = typeof(T); var parameter = new SqlParameter("Id", type.GetProperty("Id").GetValue(t) ?? DBNull.Value); string strSql = $"DELETE FROM [{type.Name}] WHERE Id = @Id"; using (SqlConnection conn = new SqlConnection(ConnectionString)) { SqlCommand command = new SqlCommand(strSql, conn); command.Parameters.Add(parameter); conn.Open(); var iRes = command.ExecuteNonQuery(); return iRes > 0 ? true : false; } } public List<T> FindAll<T>() where T : BaseModel { Type type = typeof(T); string sqlStr = $"SELECT {string.Join(",", type.GetProperties().Select(p => $"[{p.Name}]"))} FROM [{type.Name}]"; List<T> list = new List<T>(); using (SqlConnection conn = new SqlConnection(ConnectionString)) { SqlCommand command = new SqlCommand(sqlStr, conn); conn.Open(); var reader = command.ExecuteReader(); while (reader.Read()) { list.Add(this.Trans<T>(type, reader)); } } return list; } public T Find<T>(string id) where T : BaseModel { Type type = typeof(T); string sql = $"SELECT {string.Join(",", type.GetProperties().Select(p => $"[{p.Name}]"))} FROM [{type.Name}] WHERE ID = '{id}' "; object oObject = Activator.CreateInstance(type); using (SqlConnection conn = new SqlConnection(ConnectionString)) { SqlCommand command = new SqlCommand(sql, conn); conn.Open(); var reader = command.ExecuteReader(); if (reader.Read()) return this.Trans<T>(type, reader); else return default(T); } } public bool Update<T>(T t) where T : BaseModel { Type type = typeof(T); StringBuilder sb = new StringBuilder(); sb.Append($"UPDATE [{type.Name}] SET "); var propArray = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public); var propArrayLen = propArray.Count(); for (int i = 0; i < propArrayLen; i++) { var propertiesName = propArray[i].Name; if (i != propArrayLen - 1) sb.Append($"{propertiesName} = @{propertiesName}, "); else sb.Append($" {propertiesName} = @{propertiesName} "); } #region 使用foreach的写法 //foreach (var properties in propArray) //{ // var propertiesName = properties.Name; // if (i != propArrayLen) // sb.Append($"{propertiesName} = @{propertiesName}, "); // else // sb.Append($" {propertiesName} = @{propertiesName} "); // i++; //} #endregion sb.Append($" Where Id = @Id;"); var parameterList = type.GetProperties(BindingFlags.Instance | BindingFlags.Public).Select(p => new SqlParameter($"@{p.Name}", p.GetValue(t) ?? DBNull.Value)); using (SqlConnection conn = new SqlConnection(ConnectionString)) { SqlCommand command = new SqlCommand(sb.ToString(), conn); command.Parameters.AddRange(parameterList.ToArray()); conn.Open(); return command.ExecuteNonQuery() > 0; } } #region Private Method private T Trans<T>(Type type, SqlDataReader reader) { object oObject = Activator.CreateInstance(type); foreach (var properties in type.GetProperties()) { properties.SetValue(oObject, reader[properties.Name] ?? DBNull.Value); } return (T)oObject; } #endregion }
方法调用示例代码:
static void Main(string[] args) { BaseDAL baseDAL = new BaseDAL(); { List<User> users = baseDAL.FindAll<User>(); var b = baseDAL.Delete<User>(new User() { Id = "de8be521-f1ec-4483-b124-0be342890507" }); } { var b = baseDAL.Add<User>(new User { Id = Guid.NewGuid().ToString() , Name = "zs", Sex = 0, Status = 1, BizCode = "1234567", CrateId = "123456", TypeId = "123456", TypeName = "admin", CreateTime = DateTime.Now, Account = "wjl", Password = "123456" }); var s = baseDAL.Update<User>(new User { Id = "4955d7e0-808f-4d50-af66-285e2a18966e", Name = "zs", Sex = 0, Status = 1, BizCode = "test value", CrateId = "test value", TypeId = "test value", TypeName = "test value", CreateTime = DateTime.Now, Account = "test value", Password = "test value" }); } Console.ReadLine(); }
二、约束 、特性
new
约束指定泛型类声明中的类型实参必须有公共的无参数构造函数
public class Tester<T> where T:new() { public Tester() { t = new T();//等同于非泛型版本的new? 例如 object o = new object();? } private T t; }
实际上等同于下面的代码 可以不约束new()
public class Tester<T> where T:new() { public Tester() { t = System.Activator.CreateInstance<T>(); } private T t; }
特性 表名称
限制特性 只能用于 class或者字段
class