对象-关系映射,即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/archive/2022/08/24/16620088.html