【愚公系列】2023年04月 .NET_C#知识点-匿名类的自动映射

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

前言

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

var person = new { Name = "John", Age = 30 };

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

一、匿名类的自动映射

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

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

1.定义模型表

1、StudentInfo

/// <summary>
/// 学生表
/// </summary>
public class StudentInfo
{
/// <summary>
/// 标识
/// </summary>
public Guid Id { get; set; }

/// <summary>
/// 学号
/// </summary>
public string Number { get; set; }

/// <summary>
/// 姓名
/// </summary>
public string Name { get; set; }

/// <summary>
/// 班级标识
/// </summary>
public Guid ClassId { get; set; }
}

2、ClassInfo

/// <summary>
/// 班级表
/// </summary>
public class ClassInfo
{
/// <summary>
/// 标识
/// </summary>
public Guid Id { get; set; }


/// <summary>
/// 班级总人数
/// </summary>
public int TotalNumber { get; set; }
}

3、ClassStudentModel

/// <summary>
/// 列表返回模型
/// </summary>
public class ClassStudentModel
{
/// <summary>
/// 学生标识
/// </summary>
public Guid Id { get; set; }

/// <summary>
/// 学号
/// </summary>
public string Number { get; set; }

/// <summary>
/// 学生名称
/// </summary>
public string Name { get; set; }

/// <summary>
/// 班级总人数
/// </summary>
public int TotalNumber { get; set; }
}

2.非自动映射查询

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


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特性类


/// <summary>
/// 父级匿名特性
/// </summary>
public class ParentAnonymousAttribute : Attribute
{
/// <summary>
/// 构造函数
/// </summary>
public ParentAnonymousAttribute(string parentName)
{
ParentName = parentName;
}

/// <summary>
///
/// </summary>
/// <param name="parentName"></param>
/// <param name="name"></param>
public ParentAnonymousAttribute(string parentName, string name)
{
ParentName = parentName;
Name = name;
}

/// <summary>
/// 父级匿名名称
/// </summary>
public string ParentName { get; set; }

/// <summary>
/// 属性名称
/// </summary>
public string Name { get; set; }
}

3.2 AutoSelect扩展函数封装

/// <summary>
/// 自动select
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TGraph"></typeparam>
/// <param name="query"></param>
/// <param name="graph"></param>
/// <returns></returns>
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映射模型类


/// <summary>
/// 列表返回模型
/// </summary>
public class ClassStudentModel
{
/// <summary>
/// 学生标识
/// </summary>
[ParentAnonymous("s")]
public Guid Id { get; set; }

/// <summary>
/// 学号
/// </summary>
public string Number { get; set; }

/// <summary>
/// 学生名称
/// </summary>
public string Name { get; set; }

/// <summary>
/// 班级总人数
/// </summary>
public int TotalNumber { get; set; }
}

3.4 使用

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();


【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。