快速認識ORM 對象-關係映射,即Object/Relation Mapping,主要實現程式對象到關係資料庫的映射。現在.Net比較流行的ORM框架有:EF、SqlSugar、Dapper、FreeSql、Nhibernate、IBatis.Net等。 O/RM只是一層代碼的封裝,底層還是基於AD ...
快速認識ORM
對象-關係映射,即Object/Relation Mapping,主要實現程式對象到關係資料庫的映射。現在.Net比較流行的ORM框架有:EF、SqlSugar、Dapper、FreeSql、Nhibernate、IBatis.Net等。
O/RM只是一層代碼的封裝,底層還是基於ADO.NET完成對資料庫的訪問。
一般寫法
如果我們要寫一個查詢,用ADO.NET就會如下這樣寫。
private static string ConnectionStringCustomers = "Data Source=.;Database=Customers;" +
"User ID=sa;Password=123456;MultipleActiveResultSets=True";
public Company FindCompany(int id)
{
string sql = $@"
SELECT [Id],[Name],[CreateTime],[CreatorId],
[LastModifierId],[LastModifyTime]
FROM [dbo].[Company]
WHERE ID = {id}";
using (SqlConnection conn = new SqlConnection(ConnectionStringCustomers))
{
SqlCommand command = new SqlCommand(sql, conn);
conn.Open();
var reader = command.ExecuteReader();
if (reader.Read())
{
Company company = new Company()
{
Id = (int)reader["Id"],
Name = reader["Name"].ToString()
};
return company;
}
else
{
return null;
}
}
}
public abstract class BaseModel
{
public int Id { set; get; }
}
public class Company : BaseModel
{
public string Name { get; set; }
public DateTime CreateTime { get; set; }
public int CreatorId { get; set; }
public int? LastModifierId { get; set; }
public DateTime? LastModifyTime { get; set; }
}
但這樣的寫法是寫死了的,我們能不能寫一個通用查詢,不管他是哪張表。
通用寫法
既然要通用,那就不能寫死類型,我們想到了使用泛型。泛型是任何類型都可以用,為了保證類型正確,我們再加泛型約束。
為了得到屬性,我們要使用反射獲取。
public T Find<T>(int id) where T : BaseModel // 泛型約束,必須繼承自BaseModel
{
string colums = string.Join(",", typeof(T).GetProperties().Select(p => $"[{p.Name}]").ToArray());
string sql = $"SELECT {colums} FROM [{typeof(T).Name}] WHERE Id={id}";
using (SqlConnection conn = new SqlConnection(ConnectionStringCustomers))
{
SqlCommand command = new SqlCommand(sql, conn);
conn.Open();
var reader = command.ExecuteReader();
if (reader.Read())
{
// 反射實例化
T t = Activator.CreateInstance<T>();
foreach (var property in typeof(T).GetProperties())
{
property.SetValue(t, reader[property.Name] is DBNull ? null : reader[property.Name]);
}
return t;
}
else
{
return null;
}
}
return default(T);
}
上述的方法,使用泛型和反射使我們的查詢可以通用。
然後使用Company Company = sqlHelper.Find<Company>(1);
來調用得到實體。
但是,我們還有一個問題,如果我們的表名和實體類名稱不一樣,或欄位名不一樣,比如:表名為Sys_Company而實體名為Company,那我們該如何映射?
這裡我們打算用C#的特性來解決這一問題。
首先,創建用來映射的特性類。
public class AbstractMappingAttribute : Attribute
{
public string MappingName = null;
public AbstractMappingAttribute(string mappingName)
{
this.MappingName = mappingName;
}
}
映射表名。
[AttributeUsage(AttributeTargets.Class)]
public class DBProxyTableAttribute: AbstractMappingAttribute
{
public DBProxyTableAttribute(string tableName) : base(tableName){}
}
映射列名。
[AttributeUsage(AttributeTargets.Property)]
public class DBProxyColumnAttribute : AbstractMappingAttribute
{
public DBProxyColumnAttribute(string columnName):base(columnName) {}
}
在類名上添加特性。
[DBProxyTable("Sys_Company")]
public class Company : BaseModel
{
[DBProxyColumn("Company_Name")]
public string Name { get; set; }
......
}
獲取實體類名或屬性上的特性值來映射資料庫的方法。
public static class DBProxyMappingExtension
{
public static string GetMappingName(this MemberInfo member)
{
string name = null;
if (member.IsDefined(typeof(AbstractMappingAttribute), true))
{
var attribute = member.GetCustomAttribute<AbstractMappingAttribute>();
name = attribute.MappingName;
}
else
{
name = member.Name;
}
return name;
}
}
最後,重新修改通用方法。
public T Find<T>(int id) where T : BaseModel // 泛型約束,必須繼承自BaseModel
{
//string colums = string.Join(",", typeof(T).GetProperties().Select(p => $"[{p.Name}]").ToArray());
string colums = string.Join(",", typeof(T).GetProperties().Select(p => $"[{p.GetMappingName()}]").ToArray());
//string sql = $"SELECT {colums} FROM [{typeof(T).Name}] WHERE Id={id}";
string sql = $"SELECT {colums} FROM [{typeof(T).GetMappingName()}] WHERE Id={id}";
using (SqlConnection conn = new SqlConnection(ConnectionStringCustomers))
{
SqlCommand command = new SqlCommand(sql, conn);
conn.Open();
var reader = command.ExecuteReader();
if (reader.Read())
{
// 反射實例化
T t = Activator.CreateInstance<T>();
foreach (var property in typeof(T).GetProperties())
{
//property.SetValue(t, reader[property.Name] is DBNull ? null : reader[property.Name]);
property.SetValue(t, reader[property.GetMappingName()] is DBNull ? null : reader[property.GetMappingName()]);
}
return t;
}
else
{
return null;
}
}
return default(T);
}
本文來自博客園,作者:一紙年華,轉載請註明原文鏈接:https://www.cnblogs.com/nullcodeworld/p/16620088.html