【愚公系列】2023年03月 .NET/C#知识点-匿名类的自动映射

2023-03-16 19:08:51 浏览数 (1)

文章目录

  • 前言
  • 一、匿名类的自动映射
    • 1.定义模型表
    • 2.非自动映射查询
    • 3.自动映射查询
      • 3.1 ParentAnonymousAttribute特性类
      • 3.2 AutoSelect扩展函数封装
      • 3.3 改造ClassStudentModel映射模型类
      • 3.4 使用

前言

.net匿名类是一种临时创建的类,可以在运行时动态地创建。它可以用于简化代码,避免创建不必要的类。在使用匿名类时,编译器会自动为其创建一个对应的类,并将属性自动映射到该类中。这样可以方便地进行数据传递和处理,通常用于临时存储数据或传递参数。在 .NET 中,可以使用匿名类来创建一个具有一组属性的对象,这些属性可以在创建时进行初始化。例如,可以使用匿名类来创建一个包含姓名和年龄属性的对象,如下所示:

代码语言:javascript复制
var person = new { Name = "John", Age = 30 };

在这个例子中,我们创建了一个名为 person 的匿名类对象,该对象具有 Name 和 Age 两个属性,并分别初始化为 “John” 和 30。

一、匿名类的自动映射

在我们业务中经常需要使用到匿名类型,特别是数据库连表查询。因为根据业务变动需要返回字段信息不同,甚至有计算数据。因为是匿名类,在匿名类再次参加链表是关联不出属性的,这就需要进一步处理匿名类数据。

以下是匿名类的自动映射解决方案

1.定义模型表

1、StudentInfo

代码语言:javascript复制
/// 
/// 学生表
/// 
public class StudentInfo
{
    /// 
    /// 标识
    /// 
    public Guid Id { get; set; }
 
    /// 
    /// 学号
    /// 
    public string Number { get; set; }
 
    /// 
    /// 姓名
    /// 
    public string Name { get; set; }
 
    /// 
    /// 班级标识
    /// 
    public Guid ClassId { get; set; }
}

2、ClassInfo

代码语言:javascript复制
/// 
/// 班级表
/// 
public class ClassInfo
{
    /// 
    /// 标识
    /// 
    public Guid Id { get; set; }
 
 
    /// 
    /// 班级总人数
    /// 
    public int TotalNumber { get; set; }
}

3、ClassStudentModel

代码语言:javascript复制
/// 
/// 列表返回模型
/// 
public class ClassStudentModel
{
    /// 
    /// 学生标识
    /// 
    public Guid Id { get; set; }
 
    /// 
    /// 学号
    /// 
    public string Number { get; set; }
 
    /// 
    /// 学生名称
    /// 
    public string Name { get; set; }
 
    /// 
    /// 班级总人数
    /// 
    public int TotalNumber { get; set; }
}

2.非自动映射查询

对于数据库表中少量字段可以进行如下写法返回数据

代码语言:javascript复制
var query = from s in dbStore.Set<StudentInfo>()
            from c in dbStore.Set<ClassInfo>().Where(c => c.Id == s.ClassId).DefaultIfEmpty()
            select new { s, c };
 
// 一般一些业务中应该会有更多的查询条件,这里就省略了
// 比如:query = query.Where(e => e.s.Name == options.Name);
var result = query.Select(e => new ClassStudentModel
{
    Id = e.s.Id,
    Number = e.s.Number,
    Name = e.s.Name,
    TotalNumber = e.c.TotalNumber
}).ToList();

3.自动映射查询

使用表达式树 反射可以实现此需求,通过反射将各模型中的字段名与列表返回模型中的各字段进行对应,再利用表达式树进行拼接构造函数。

对于两个类有相同字段可以进行如下处理:比如说学生表里有Id,班级表中也有Id,可以加了一个特性自动映射ParentAnonymousAttribute,通过此特性来判别取哪个模型中的Id,具体代码如下:

3.1 ParentAnonymousAttribute特性类

代码语言:javascript复制
/// 
/// 父级匿名特性
/// 
public class ParentAnonymousAttribute : Attribute
{
    /// 
    /// 构造函数
    /// 
    public ParentAnonymousAttribute(string parentName)
    {
        ParentName = parentName;
    }
 
    /// 
    ///
    /// 
    /// 
    /// 
    public ParentAnonymousAttribute(string parentName, string name)
    {
        ParentName = parentName;
        Name = name;
    }
 
    /// 
    /// 父级匿名名称
    /// 
    public string ParentName { get; set; }
 
    /// 
    /// 属性名称
    /// 
    public string Name { get; set; }
}

3.2 AutoSelect扩展函数封装

代码语言:javascript复制
/// 
/// 自动select
/// 
/// 
/// 
/// 
/// 
/// 
public static IQueryable<TGraph> AutoSelect<T, TGraph>(this IQueryable<T> query, TGraph graph)
{
    var defaultCtor = typeof(TGraph).GetConstructor(Type.EmptyTypes);
    if (defaultCtor == null)
    {
        throw new Exception();
    }
    var constructor = Expression.New(defaultCtor);
    var bindings = new List<MemberAssignment>();
    var tExp = Expression.Parameter(typeof(T), "t");
    var typeT = typeof(T);
    var tProperties = typeT.GetProperties();
    var graphProperties = typeof(TGraph).GetProperties().Where(e => e.SetMethod != null).ToList();
    var graphPointParentProps = graphProperties.Where(e => e.GetCustomAttribute<ParentAnonymousAttribute>() != null).ToList();
    var graphNoPointParentProps = graphProperties.Where(e => e.GetCustomAttribute<ParentAnonymousAttribute>() == null).ToList();
    foreach (var item in graphPointParentProps)
    {
        var attr = item.GetCustomAttribute<ParentAnonymousAttribute>();
        var pExp = Expression.Property(tExp, attr.ParentName);
        var realExp = Expression.Property(pExp, attr.Name ?? item.Name);
        bindings.Add(Expression.Bind(item, realExp));
    }
    foreach (var prop in tProperties)
    {
        var anoyClass = prop.PropertyType.GetProperties();
        foreach (var item in anoyClass)
        {
            if (graphNoPointParentProps.Any(a => a.Name == item.Name))
            {
                var property = graphNoPointParentProps.Where(a => a.Name == item.Name).FirstOrDefault();
                if (property.PropertyType != item.PropertyType)
                {
                    continue;
                }
                if (bindings.Any(e => e.Member.Name == property.Name))
                {
                    continue;
                }
                var pExp = Expression.Property(tExp, prop.Name);
                var realExp = Expression.Property(pExp, item.Name);
                bindings.Add(Expression.Bind(property, realExp));
            }
        }
    }
 
    var init = Expression.MemberInit(constructor, bindings);
    var final = Expression.Lambda<Func<T, TGraph>>(init, tExp);
    return query.Select(final);
}

3.3 改造ClassStudentModel映射模型类

代码语言:javascript复制
/// 
/// 列表返回模型
/// 
public class ClassStudentModel
{
    /// 
    /// 学生标识
    /// 
    [ParentAnonymous("s")]
    public Guid Id { get; set; }
 
    /// 
    /// 学号
    /// 
    public string Number { get; set; }
 
    /// 
    /// 学生名称
    /// 
    public string Name { get; set; }
 
    /// 
    /// 班级总人数
    /// 
    public int TotalNumber { get; set; }
}

3.4 使用

代码语言:javascript复制
var query = from s in dbStore.Set<StudentInfo>()
            from c in dbStore.Set<ClassInfo>().Where(c => c.Id == s.ClassId).DefaultIfEmpty()
            select new { s, c };
 
var result = query.AutoSelect(new ClassStudentModel()).ToList();

0 人点赞