• 作者:一纸年华
  • 分类: .Net

快速认识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/archive/2022/08/24/16620088.html