既然是反射,那就来一个彻底一点的!
我们不是要给对应的Entity写AppService作为RUSTAPI么
那可不可以不写,用统一的?
需要个性化的再写!
这个需求是可以的!
把下面这个放在项目的XXX.Application项目下,结合自己的项目进行调整即可!
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using PasteForm.Application.Contracts;
using PasteForm.Handler;
using PasteForm.usermodels;
using PasteFormHelper;
using System.Collections;
using System.ComponentModel.DataAnnotations;
using System.Dynamic;
using System.Linq.Dynamic.Core;
using System.Linq.Expressions;
using System.Reflection;
using System.Text.Json.Nodes;
using System.Web;
using Volo.Abp.Auditing;
namespace PasteForm.Application
{
/// <summary>
/// 默认读取 这个很关键,实现了统一的新增 更新 详细 列表规范读取 相当于兜底的!
/// Version.2025.07.06.02
/// 当前还有一个问题,就是Page不是采用LeftJoin的,是分次查询,因为LeftJoin的组合有问题,目前还在找办法,Queryable死活会被转换成DbSet... .. .
///</summary>
[TypeFilter(typeof(RoleAttribute), Arguments = new object[] { "data", "view" })]
[DisableAuditing]
public class DefaultAppService : PasteFormAppService
{
#region 以下是具体实现 Version.2025.07.03
/// <summary>
/// 读取AddDto的数据模型 用于新增
/// </summary>
/// <returns></returns>
[HttpGet]
[TypeFilter(typeof(RoleAttribute), Arguments = new object[] { "data", "view" })]
[Route("/api/app/{entityName}/readAddModel", Order = 10)]
public VoloModelInfo ReadAddModel([FromRoute] string entityName)
{
// 1. 根据实体名称查找对应的实体类型和 DTO 类型
var entityType = FindEntityType(entityName);
if (entityType == null)
{
throw new PasteCodeException($"找不到对应的实体类型: {entityName}");
}
var dtoType = FindDtoType(entityName, "AddDto");
if (dtoType == null)
{
throw new PasteCodeException($"找不到对应的 DTO 类型: {entityName}AddDto");
}
//new一个出来
var dto = Activator.CreateInstance(dtoType);
// 4. 动态读取 DTO 的属性信息
var dataModel = ReadModelProperties(dto, dtoType);
return dataModel;
}
/// <summary>
/// 用于更新表单
/// </summary>
/// <param name="entityName"></param>
/// <param name="id"></param>
/// <returns></returns>
/// <exception cref="PasteCodeException"></exception>
[HttpGet]
[TypeFilter(typeof(RoleAttribute), Arguments = new object[] { "data", "view" })]
[Route("/api/app/{entityName}/{id}/readUpdateModel", Order = 10)]
public async Task<VoloModelInfo> ReadUpdateModel([FromRoute] string entityName, [FromRoute] int id)
{
// 1. 根据实体名称查找对应的实体类型和 DTO 类型
var entityType = FindEntityType(entityName);
var dtoType = FindDtoType(entityName, "UpdateDto");
if (entityType == null || dtoType == null)
{
throw new PasteCodeException($"找不到对应的实体或 DTO 类型: {entityName}");
}
// 2. 动态查询实体
var entity = await FindEntityByIdAsync(entityType, id);
if (entity == null)
{
throw new PasteCodeException("查询的信息不存在,无法执行编辑操作!");
}
// 3. 动态映射实体到 DTO
var dto = MapEntityToDto(entity, entityType, dtoType);
//构建外表等 Include的如何搞定???
await BuildOuterQuery(dto, dtoType);
// 4. 动态读取 DTO 的属性信息
var dataModel = ReadModelProperties(dto, dtoType);
return dataModel;
}
/// <summary>
/// 用于查看详情
/// </summary>
/// <returns></returns>
[HttpGet]
[Route("/api/app/{entityName}/{id}/readDetailModel", Order = 10)]
[TypeFilter(typeof(RoleAttribute), Arguments = new object[] { "data", "view" })]
public async Task<VoloModelInfo> ReadDetailModel([FromRoute] string entityName, [FromRoute] int id)
{
//var _info = await _dbContext.StoreCashWay.Where(x => x.Id == id).AsNoTracking().FirstOrDefaultAsync();
//if (_info == null || _info == default)
//{
// throw new PasteCodeException("查询的信息不存在,无法执行编辑操作!");
//}
//var dto = ObjectMapper.Map<StoreCashWay, StoreCashWayDto>(_info);
//var _dataModel = PasteBuilderHelper.ReadModelProperty<StoreCashWayDto>(dto);
//return _dataModel;
// 1. 根据实体名称查找对应的实体类型和 DTO 类型
var entityType = FindEntityType(entityName);
var dtoType = FindDtoType(entityName, "Dto");
if (entityType == null || dtoType == null)
{
throw new PasteCodeException($"找不到对应的实体或 DTO 类型: {entityName}");
}
// 2. 动态查询实体
var entity = await FindEntityByIdAsync(entityType, id);
if (entity == null)
{
throw new PasteCodeException("查询的信息不存在,无法执行编辑操作!");
}
// 3. 动态映射实体到 DTO
var dto = MapEntityToDto(entity, entityType, dtoType);
//构建外表等 Include的如何搞定???
await BuildOuterQuery(dto, dtoType);
// 4. 动态读取 DTO 的属性信息
var dataModel = ReadModelProperties(dto, dtoType);
return dataModel;
}
/// <summary>
/// 用户查看表格
/// </summary>
/// <returns></returns>
[HttpGet]
[Route("/api/app/{entityName}/readListModel", Order = 10)]
[TypeFilter(typeof(RoleAttribute), Arguments = new object[] { "data", "view" })]
public VoloModelInfo ReadListModel([FromRoute] string entityName, string searchName = "InputSearchBase")
{
// 动态查找实体对应的 ListDto 类型
var listDtoType = FindDtoType(entityName, "ListDto");
// 创建 ListDto 实例
var listDtoInstance = Activator.CreateInstance(listDtoType);
// 动态读取 ListDto 的属性信息
//var listModel = PasteBuilderHelper.ReadModelProperty(listDtoInstance, true);
var listModel = ReadModelProperties(listDtoInstance, listDtoType, true);
if (searchName != "InputSearchBase")
{
// 保持搜索模型的静态处理(假设 InputSearchBase 是固定类)
var searchModel = PasteBuilderHelper.ReadModelProperty(new InputSearchBase());
if (searchModel != null)
{
listModel.QueryProperties = searchModel.Properties;
}
}
else
{
var search_type = FindType(searchName);
if (search_type != null)
{
var search_model = Activator.CreateInstance(search_type);
//var searchModel = PasteBuilderHelper.ReadModelProperty(search_model);
var searchModel = ReadModelProperties(search_model, search_type, true);
if (searchModel != null)
{
listModel.QueryProperties = searchModel.Properties;
}
}
}
return listModel;
}
/// <summary>
/// 简单按页查询 不涉及Left Join等
/// </summary>
/// <param name="entityName"></param>
/// <param name="searchName"></param>
/// <returns></returns>
/// <exception cref="PasteCodeException"></exception>
[HttpGet]
[Route("/api/app/{entityName}/page", Order = 10)]
[TypeFilter(typeof(RoleAttribute), Arguments = new object[] { "data", "view" })]
public async Task<dynamic> Page([FromRoute] string entityName, string searchName = "InputSearchBase")
{
// [FromQuery] InputSearchBase input
// 1. 根据实体名称查找对应的实体类型和DTO类型
var entityType = FindEntityType(entityName);
var listDtoType = FindDtoType(entityName, "ListDto");
if (entityType == null || listDtoType == null)
{
throw new PasteCodeException($"找不到对应的实体或DTO类型: {entityName}", 404);
}
var query_string = base._httpContext.Request.QueryString.ToString();
var querys = query_string.ToQuerys();
foreach (var key in querys.Keys)
{
querys[key] = HttpUtility.UrlDecode(querys[key]);
}
var query_body = JsonConvert.SerializeObject(querys);
//获取基础的
var input = JsonConvert.DeserializeObject<InputSearchBase>(query_body);
//Console.WriteLine("line.219");
Type searchType = null;
object searchObject = null;
if (searchName != "InputSearchBase")
{
searchType = FindType(searchName);
searchObject = JsonConvert.DeserializeObject(query_body, searchType);
//未完待续,后续扩展,更多其他字段查询
}
//Console.WriteLine("line.229");
IQueryable query = null;
if (searchName == "InputSearchBase")
{
// 2. 动态构建查询
query = BuildDynamicQuery(entityType, input);
}
else
{
query = BuildDynamicQuery(entityType, searchType, searchObject);
}
// 3. 计算总记录数(仅第一页需要)
//var pagedDtoType = typeof(PagedResultDto<>).MakeGenericType(listDtoType);
//dynamic pagedDto = Activator.CreateInstance(pagedDtoType);
//Console.WriteLine("line.244");
dynamic pagedto = new ExpandoObject();
if (input.page == 1)
{
pagedto.totalCount = await ExecuteCountQuery(query, entityType);
}
if (searchName == "InputSearchBase")
{
query = ApplyPagingAndSorting(query, entityType, input);
// 4. 应用分页和排序
//var pagedQuery = ApplyPagingAndSorting(query, entityType, input);
}
else
{
query = ApplyPagingAndSorting(query, entityType, searchType, searchObject);
}
// 5. 执行查询并获取结果
//var entities = await ExecuteQuery(query, entityType);
//if (entities == null || entities.Count == 0)
//{
// return null;
//}
//var dtos = MapEntitiesToDtos(entities, entityType, listDtoType);
var dtos = await ExecuteProjectionQuery(query, entityType, listDtoType);
if (dtos?.Any() == false)
{
return null;
}
// 6. 动态映射实体到DTO
//查询外表 并实现 这里是简单的外表ID的查询 如果有更复杂的,还需要实现
await BuildOuterData(listDtoType, dtos);
// 7. 设置结果并返回
//pagedDto.Items = dtos.Cast<object>().ToList();
pagedto.items = dtos;
return pagedto;
}
/// <summary>
/// 通用创建方法
/// </summary>
/// <param name="entityName"></param>
/// <param name="input"></param>
/// <returns></returns>
/// <exception cref="PasteCodeException"></exception>
[HttpPost]
[Route("/api/app/{entityName}/item", Order = 10)]
[TypeFilter(typeof(RoleAttribute), Arguments = new object[] { "data", "add" })]
public async Task<dynamic> CreateItemAsync([FromRoute] string entityName, [FromBody] JsonObject input)
{
// 获取实体类型和添加DTO类型
var entityType = FindEntityType(entityName);
var addDtoType = FindDtoType(entityName, "AddDto");
if (entityType == null || addDtoType == null)
{
throw new PasteCodeException($"找不到对应的实体或添加DTO类型: {entityName}", 404);
}
////Console.WriteLine($"line.769 {input.ToString()} {input.ToJsonString()}");
// 反序列化输入数据到添加DTO
var addDto = JsonConvert.DeserializeObject(input.ToJsonString(), addDtoType);
ValidateModel(addDto, addDtoType);
// 创建实体实例
var entity = Activator.CreateInstance(entityType);
// 映射DTO到实体
MapProperties(addDto, entity, addDtoType, entityType);
//填充预定值
//foreach (var pro in entityType.GetProperties())
//{
// switch (pro.Name)
// {
// case "UserId":
// case "CreateUserId":
// {
// var user_id = base.ReadCurrentUserId();
// SetPropertyValue(entity, pro.Name, user_id);
// }
// break;
// default:
// break;
// }
//}
//下面这个和上面注释的意思一样
base.DefaultBeforeCreate(entityType.GetProperties(),entity);
// 添加到数据库
var dbSet = GetDbSet(entityType);
dbSet.GetType().GetMethod("Add").Invoke(dbSet, new[] { entity });
// 保存更改
await _dbContext.SaveChangesAsync();
//Console.WriteLine("line.869");
var find_id = entityType.GetProperties().Where(x => x.Name == "Id").FirstOrDefault();
if (find_id != null && find_id != default)
{
return find_id.GetValue(entity).ToString();
}
return null;
//// 映射实体到返回DTO
//var returnDtoType = FindDtoType(entityName, "Dto");
//var returnDto = Activator.CreateInstance(returnDtoType);
//MapProperties(entity, returnDto, entityType, returnDtoType);
//return returnDto;
}
/// <summary>
/// 通用更新方法
/// </summary>
/// <param name="entityName"></param>
/// <param name="input"></param>
/// <returns></returns>
/// <exception cref="PasteCodeException"></exception>
[HttpPost]
[Route("/api/app/{entityName}/updateItem", Order = 10)]
[TypeFilter(typeof(RoleAttribute), Arguments = new object[] { "data", "edit" })]
public async Task<object> UpdateItemAsync([FromRoute] string entityName, [FromBody] JsonObject input)
{
// 获取实体类型和更新DTO类型
var entityType = FindEntityType(entityName);
var updateDtoType = FindDtoType(entityName, "UpdateDto");
if (entityType == null || updateDtoType == null)
{
throw new PasteCodeException($"找不到对应的实体或更新DTO类型: {entityName}", 404);
}
// 反序列化输入数据到更新DTO
var updateDto = JsonConvert.DeserializeObject(input.ToString(), updateDtoType);
// 手动执行模型验证
ValidateModel(updateDto, updateDtoType);
// 获取ID属性值
var id = GetPropertyValue(updateDto, "Id");
if (id == null)
{
throw new PasteCodeException("更新DTO必须包含Id属性", 400);
}
// 查找实体
var entity = await FindEntityByIdAsync(entityType, id);
if (entity == null)
{
throw new PasteCodeException("需要更新的记录不存在", 404);
}
// 映射DTO到实体
MapProperties(updateDto, entity, updateDtoType, entityType);
//foreach (var pro in entityType.GetProperties())
//{
// switch (pro.Name)
// {
// case "UpdateTime":
// case "UpdateDate":
// {
// SetPropertyValue(entity, pro.Name, DateTime.Now);
// }
// break;
// default:
// break;
// }
//}
//下面这个和上面注释的意思一样
base.DefaultBeforeUpdate(entityType.GetProperties(), entity);
// 保存更改
await _dbContext.SaveChangesAsync();
//var find_id = entityType.GetProperties().Where(x => x.Name == "Id").FirstOrDefault();
//if (find_id != null && find_id != default)
//{
// return find_id.GetValue(dbSet);
//}
return id;
//// 映射实体到返回DTO
//var returnDtoType = FindDtoType(entityName, "Dto");
//var returnDto = Activator.CreateInstance(returnDtoType);
//MapProperties(entity, returnDto, entityType, returnDtoType);
//return returnDto;
}
/// <summary>
/// 通用状态更新方法
/// </summary>
/// <param name="entityName"></param>
/// <param name="input"></param>
/// <returns></returns>
/// <exception cref="PasteCodeException"></exception>
[HttpPost]
[Route("/api/app/{entityName}/updateState", Order = 10)]
[TypeFilter(typeof(RoleAttribute), Arguments = new object[] { "data", "state" })]
public async Task<string> UpdateState([FromRoute] string entityName, [FromBody] InputQueryUpdateState input)
{
// 获取实体类型
var entityType = FindEntityType(entityName);
if (entityType == null)
{
throw new PasteCodeException($"找不到对应的实体类型: {entityName}", 404);
}
// 解析ID
if (!int.TryParse(input.id, out var id))
{
throw new PasteCodeException("提供的参数id错误,无法继续执行", 400);
}
// 查找实体
var entity = await FindEntityByIdAsync(entityType, id);
if (entity == null)
{
throw new PasteCodeException("没有找到对应的操作对象,无法继续执行", 404);
}
// 获取实体的所有布尔类型属性
var booleanProperties = entityType.GetProperties()
.Where(p => p.PropertyType == typeof(bool) || p.PropertyType == typeof(bool?))
.ToDictionary(p => p.Name, p => p);
//if (!booleanProperties.ContainsKey(input.name.ToLower()))
//{
// throw new PasteCodeException($"实体 {entityName} 中不存在布尔属性: {input.name}", 400);
//}
//input.name isEnable转 IsEnable
var current_pro = ConvertToPascalCase(input.name);
if (booleanProperties.ContainsKey(current_pro))
{
// 获取属性信息并更新值
var property = booleanProperties[current_pro];
if (!property.CanWrite)
{
throw new PasteCodeException($"属性 {input.name} 不可写", 400);
}
// 处理可空布尔类型
if (property.PropertyType == typeof(bool?))
{
property.SetValue(entity, input.state ? (bool?)true : (bool?)false);
}
else
{
property.SetValue(entity, input.state);
}
// 保存更改
await _dbContext.SaveChangesAsync();
}
else
{
Logger.LogWarning($"没有找到这个字段信息:{current_pro} {input.name}");
}
return "提交成功";
}
/// <summary>
/// 通用删除 遵照ListDto的PasteDisable特性
/// </summary>
/// <param name="entityName"></param>
/// <param name="id"></param>
/// <returns></returns>
/// <exception cref="PasteCodeException"></exception>
[HttpPost]
[Route("/api/app/{entityName}/remove", Order = 10)]
[TypeFilter(typeof(RoleAttribute), Arguments = new object[] { "data", "del" })]
public async Task<string> Remove([FromRoute] string entityName, [FromQuery] int id)
{
// 获取实体类型
var entityType = FindEntityType(entityName);
if (entityType == null)
{
throw new PasteCodeException($"找不到对应的实体类型: {entityName}", 404);
}
var listDtoType = FindDtoType(entityName, "ListDto");
if (listDtoType != null)
{
var finds = listDtoType.GetCustomAttributes<PasteDisableAttribute>();
if (finds?.Any() == true)
{
var find = finds.FirstOrDefault();
if (find.Args3 == "del")
{
throw new PasteCodeException($"实体 {entityName} 不允许删除操作", 403);
}
}
}
// 查找实体
var entity = await FindEntityByIdAsync(entityType, id);
if (entity == null)
{
throw new PasteCodeException("需要删除的记录不存在", 404);
}
// 执行删除操作
var dbSet = GetDbSet(entityType);
dbSet.GetType().GetMethod("Remove").Invoke(dbSet, new[] { entity });
// 保存更改
await _dbContext.SaveChangesAsync();
return "删除成功";
}
#region 以下是辅助函数
/// <summary>
///
/// </summary>
/// <param name="dto"></param>
/// <param name="dtoType"></param>
/// <returns></returns>
private async Task BuildOuterQuery(object dto, Type dtoType)
{
#region 看看是否要执行外表查询
foreach (var aty in dtoType.GetProperties())
{
//var _type = aty.PropertyType;
var attributes = aty.GetCustomAttributes<PasteShortAttribute>();
if (attributes?.Any() == true)
{
var short_attr = attributes.FirstOrDefault();
if (short_attr != null && short_attr != default)
{
var foreignKeyId = aty.GetValue(dto);
if (foreignKeyId == null || foreignKeyId.Equals(0))
{
continue;
}
var shortAttr = short_attr;
//var pro_val =//??? 当前字段的值
//short_attr.Args1 = "GradeInfo";//表示对应的外表的EntityName
//short_attr.Args2 = "ExtendGrade";//表示结果写入到这个字段
//short_attr.Args3 = "ToShortUser()";//表示要转换的类型 比如_dbContext.GradeInfo.Where(x=>x.Id==pro_val).Select(x=>x.ToShortUser()).FirstOrDefaultAsync();
//如何获取字段的当前值?
//await _dbContext.GradeInfo.Where(x => x.Id == 1).Select(y => y.ToShortGrade()).FirstOrDefaultAsync();
//ToShortGrade()是GradeInfo的扩展,返回的就是ExtendGrade的类型
// 获取外表的Entity类型和目标属性
var relatedEntityType = FindEntityType(shortAttr.Args1);
var targetProperty = dtoType.GetProperty(shortAttr.Args2);
if (relatedEntityType == null || targetProperty == null)
{
continue;
}
try
{
// 获取关联实体的DbSet
var dbSet = _dbContext.GetType()
.GetProperties()
.First(p => p.PropertyType.GenericTypeArguments.Contains(relatedEntityType))
.GetValue(_dbContext);
// 动态调用FindAsync方法获取关联实体
var findAsyncMethod = dbSet.GetType()
.GetMethod("FindAsync", new[] { typeof(object[]) });
// 执行查询
var valueTask = findAsyncMethod.Invoke(dbSet, new object[] { new object[] { foreignKeyId } });
// 获取ValueTask的结果
object relatedEntity;
// 检查是否是ValueTask
if (valueTask.GetType().Name.StartsWith("ValueTask"))
{
// 手动获取ValueTask的结果
var getAwaiterMethod = valueTask.GetType().GetMethod("GetAwaiter");
var awaiter = getAwaiterMethod.Invoke(valueTask, null);
var getResultMethod = awaiter.GetType().GetMethod("GetResult");
relatedEntity = getResultMethod.Invoke(awaiter, null);
}
else
{
// 普通Task,直接等待
await (Task)valueTask;
relatedEntity = ((Task<object>)valueTask).Result;
}
if (relatedEntity != null)
{
// 处理转换逻辑
object convertedResult = null;
// 检查是否需要特殊转换
if (!string.IsNullOrEmpty(shortAttr.Args3))
{
var extend = typeof(LinqExtend).GetMethod(shortAttr.Args3.Replace("()", ""), new[] { relatedEntityType });
// 处理特殊转换方法(例如:ToShortUser())
//convertedResult = ConvertEntity(relatedEntity, relatedEntityType, shortAttr.Args3);
convertedResult = extend.Invoke(null, new object[] { relatedEntity });
}
else
{
// 默认转换:直接使用目标属性类型
convertedResult = relatedEntity;
}
// 设置目标属性的值
targetProperty.SetValue(dto, convertedResult);
}
}
catch (Exception ex)
{
Logger.LogError(ex, $"加载关联数据失败: {shortAttr.Args1}");
// 可以选择记录错误但继续处理其他属性
}
//------------------------------------------------
}
}
}
#endregion
}
/// <summary>
///
/// </summary>
/// <param name="entityName"></param>
/// <returns></returns>
private Type FindEntityType(string entityName)
{
// 将前端传递的名称转换为 PascalCase (例如: storeCashWay -> StoreCashWay)
var pascalCaseName = ConvertToPascalCase(entityName);
var assembly = typeof(UserInfo).Assembly;//这里对不对呢... .. .
return assembly.GetTypes().FirstOrDefault(t => t.Name == entityName || t.Name == $"{pascalCaseName}");//这里可能有问题了
}
/// <summary>
///
/// </summary>
/// <param name="entityName"></param>
/// <param name="suffix"></param>
/// <returns></returns>
private Type FindDtoType(string entityName, string suffix)
{
var pascalCaseName = ConvertToPascalCase(entityName);
var dtoName = $"{pascalCaseName}{suffix}";
return typeof(InputSearchBase).Assembly.GetTypes().FirstOrDefault(t => t.Name == dtoName);
}
/// <summary>
///
/// </summary>
/// <param name="typeName"></param>
/// <returns></returns>
private Type FindType(string typeName)
{
var assembly = Assembly.GetExecutingAssembly();
//return assembly.GetTypes().FirstOrDefault(t => t.Name == typeName);
return typeof(InputSearchBase).Assembly.GetTypes().FirstOrDefault(t => t.Name == typeName);
}
/// <summary>
///
/// </summary>
/// <param name="entityType"></param>
/// <param name="id"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
/// <exception cref="PasteCodeException"></exception>
private async Task<object> FindEntityByIdAsync(Type entityType, object id, CancellationToken cancellationToken = default)
{
// 获取实体对应的 DbSet 属性
var dbSetProperty = _dbContext.GetType()
.GetProperties()
.FirstOrDefault(p =>
p.PropertyType.IsGenericType &&
p.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>) &&
p.PropertyType.GetGenericArguments()[0] == entityType);
if (dbSetProperty == null)
{
throw new PasteCodeException($"找不到实体 {entityType.Name} 的 DbSet");
}
// 获取 DbSet 实例
var dbSet = dbSetProperty.GetValue(_dbContext);
// 获取 FindAsync 方法
var findAsyncMethod = dbSet.GetType()
.GetMethod("FindAsync", new[] { typeof(object[]), typeof(CancellationToken) });
// 调用方法
var valueTask = findAsyncMethod.Invoke(dbSet, new object[] { new object[] { id }, cancellationToken });
// 处理 ValueTask<T>
if (valueTask is Task task)
{
await task;
var resultProperty = task.GetType().GetProperty("Result");
return resultProperty.GetValue(task);
}
else
{
// 手动处理 ValueTask<T>
var getAwaiterMethod = valueTask.GetType().GetMethod("GetAwaiter");
var awaiter = getAwaiterMethod.Invoke(valueTask, null);
var getResultMethod = awaiter.GetType().GetMethod("GetResult");
return getResultMethod.Invoke(awaiter, null);
}
}
/// <summary>
///
/// </summary>
/// <param name="sourceType"></param>
/// <param name="destinationType"></param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
private MethodInfo GetMapMethod(Type sourceType, Type destinationType)
{
// 获取 Map<TSource, TDestination> 方法定义
var map = base.ObjectMapper;
var mapMethod = typeof(Volo.Abp.ObjectMapping.IObjectMapper)
.GetMethods(BindingFlags.Public | BindingFlags.Instance)
.FirstOrDefault(m =>
m.Name == "Map" &&
m.IsGenericMethodDefinition &&
m.GetGenericArguments().Length == 2 &&
m.GetParameters().Length == 1
);
if (mapMethod == null)
{
throw new InvalidOperationException("未找到 Map<TSource, TDestination> 方法");
}
// 构造具体的泛型方法
return mapMethod.MakeGenericMethod(sourceType, destinationType);
}
/// <summary>
///
/// </summary>
/// <param name="sourceInstance"></param>
/// <param name="sourceType"></param>
/// <param name="destinationType"></param>
/// <returns></returns>
public object MapEntityToDto(object sourceInstance, Type sourceType, Type destinationType)
{
// 获取 ObjectMapper 实例
var mapper = ObjectMapper;
// 获取映射方法
var mapMethod = GetMapMethod(sourceType, destinationType);
// 调用映射方法
return mapMethod.Invoke(ObjectMapper, new object[] { sourceInstance });
}
/// <summary>
/// 动态读取模型属性
/// </summary>
/// <param name="model"></param>
/// <param name="modelType"></param>
/// <param name="IsListModel">针对枚举的备注过滤</param>
/// <returns></returns>
private VoloModelInfo ReadModelProperties(object model, Type modelType, bool IsListModel = false)
{
// 获取目标泛型方法定义
var genericMethodDefinition = typeof(PasteBuilderHelper)
.GetMethods(BindingFlags.Public | BindingFlags.Static)
.First(m => m.Name == "DynamicReadModelProperty" && m.IsGenericMethodDefinition);
// 构建具体的泛型方法(传入实际类型参数)
var constructedGenericMethod = genericMethodDefinition
.MakeGenericMethod(modelType);
// 调用方法并转换结果
return (VoloModelInfo)constructedGenericMethod
.Invoke(null, new object[] { model, IsListModel });
}
/// <summary>
///
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
private string ConvertToPascalCase(string input)
{
if (string.IsNullOrEmpty(input))
{
return input;
}
if (char.IsUpper(input[0]))
{
return input;
}
return input.Substring(0, 1).ToUpper() + input.Substring(1);
}
#endregion
/// <summary>
///
/// </summary>
/// <param name="model"></param>
/// <param name="modelType"></param>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="PasteCodeException"></exception>
private void ValidateModel(object model, Type modelType)
{
if (model == null)
throw new ArgumentNullException(nameof(model));
var validationResults = new List<ValidationResult>();
var validationContext = new ValidationContext(model);
if (!Validator.TryValidateObject(model, validationContext, validationResults, true))
{
var errorMessages = validationResults
.SelectMany(r => r.MemberNames.Select(n => $"{n}: {r.ErrorMessage}"))
.ToList();
throw new PasteCodeException("输入数据验证失败", 400);
}
else
{
//Console.WriteLine($"{modelType.FullName} 验证通过!");
}
}
/// <summary>
/// 补充Extend外表查询 比如ExtendUser 可能有多个
/// </summary>
/// <param name="listDtoType"></param>
/// <param name="dtos"></param>
/// <returns></returns>
private async Task BuildOuterData(Type listDtoType, List<object> dtos)
{
var sears = listDtoType.GetProperties().Where(x => x.GetCustomAttributes<PasteShortAttribute>().Any()).ToList();
if (sears?.Any() == true)
{
foreach (var _search in sears)
{
var short_attr = _search.GetCustomAttributes<PasteShortAttribute>().FirstOrDefault();
if (short_attr != null && short_attr != default)
{
var out_id_field = _search.Name;//UserId
var out_entity_name = short_attr.Args1;//UserInfo
var to_extend = short_attr.Args2;//ExtendUser
var target_property = listDtoType.GetProperties().Where(x => x.Name == to_extend).FirstOrDefault();
if (target_property == null || target_property == default)
{
continue;
}
var relatedDtoType = target_property.PropertyType;
var ids = new HashSet<object>();
foreach (var dto in dtos)
{
var id = dto.GetType().GetProperty(out_id_field)?.GetValue(dto);
if (id != null)
{
ids.Add(id);
}
}
if (ids.Count > 0)
{
//从数据库读取数据
var relatedEntityType = FindEntityType(out_entity_name);
//转换成对应的dto
//赋值
if (relatedEntityType == null)
{
continue;
}
// 构建ID查询条件
var relatedQuery = BuildIdQuery(relatedEntityType, ids);
//先查询出Entitys再Mapper
//var relatedEntities = await ExecuteQuery(relatedQuery, relatedEntityType);
//if (relatedEntities?.Any() == false)
//{
// continue;
//}
//var relatedDtos = MapEntitiesToDtos(relatedEntities, relatedEntityType, relatedDtoType);
//直接查询Dto
var relatedDtos = await ExecuteProjectionQuery(relatedQuery, relatedEntityType, relatedDtoType);
// 3. 创建ID到DTO的映射字典
var idToDto = relatedDtos.ToDictionary(d => d.GetType().GetProperty("Id")?.GetValue(d));
// 4. 填充到主表DTO
foreach (var dto in dtos)
{
//获取UserId的值
var id = dto.GetType().GetProperty(out_id_field)?.GetValue(dto);
if (id != null && idToDto.TryGetValue(id, out var relatedDto))
{
//给ExtendUser填充值
dto.GetType().GetProperty(to_extend)?.SetValue(dto, relatedDto);
}
}
}
}
}
}
}
#region Page的辅助方法
/// <summary>
/// 动态构建查询的方法
/// </summary>
/// <param name="entityType"></param>
/// <param name="input"></param>
/// <returns></returns>
private IQueryable BuildDynamicQuery(Type entityType, InputSearchBase input)
{
// 获取DbSet方法
var dbSetMethod = _dbContext.GetType()
.GetMethods()
.First(m => m.Name == "Set" && m.IsGenericMethod);
// 调用DbSet<TEntity>方法
var genericDbSetMethod = dbSetMethod.MakeGenericMethod(entityType);
var dbSet = genericDbSetMethod.Invoke(_dbContext, null);
// 获取IQueryable接口
var queryable = (IQueryable)dbSet;
// 应用过滤条件
queryable = ApplyFilterConditions(queryable, entityType, input.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance), input);
return queryable;
}
/// <summary>
///
/// </summary>
/// <param name="entityType"></param>
/// <param name="searchType"></param>
/// <param name="input"></param>
/// <returns></returns>
private IQueryable BuildDynamicQuery(Type entityType, Type searchType, object input)
{
// 获取DbSet方法
var dbSetMethod = _dbContext.GetType()
.GetMethods()
.First(m => m.Name == "Set" && m.IsGenericMethod);
// 调用DbSet<TEntity>方法
var genericDbSetMethod = dbSetMethod.MakeGenericMethod(entityType);
var dbSet = genericDbSetMethod.Invoke(_dbContext, null);
// 获取IQueryable接口
var queryable = (IQueryable)dbSet;
// 应用过滤条件
queryable = ApplyFilterConditions(queryable, entityType, searchType.GetProperties(BindingFlags.Public | BindingFlags.Instance), input);
return queryable;
}
/// <summary>
/// 应用过滤条件的方法
/// </summary>
/// <param name="query"></param>
/// <param name="entityType"></param>
/// <param name="properties"></param>
/// <param name="input"></param>
/// <returns></returns>
private IQueryable ApplyFilterConditions(IQueryable query, Type entityType, PropertyInfo[] properties, object input)
{
// 获取输入对象的所有属性
//var properties = input.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
var ignors = new List<string> { "page", "size", "orderby" };
foreach (var property in properties)
{
if (ignors.Contains(property.Name))
{
continue;
}
// 跳过不需要作为过滤条件的属性
//if (property.Name == "page" || property.Name == "size" || property.Name == "orderby" || property.GetValue(input) == null)
//{
// continue;
//}
// 获取属性值
var value = property.GetValue(input);
if (IsDefaultValue(value, property.PropertyType))
{
continue;
}
// 特殊处理搜索关键词
if (property.Name == "word" && !string.IsNullOrEmpty(value?.ToString()))
{
//是否要一个字段标记这个?
query = ApplySearchKeyword(query, entityType, value.ToString());
continue;
}
//Logger.LogInformation($"Search Property.Name: {property.Name} Type:{property.PropertyType.Name} FullName:{property.PropertyType.FullName}");
var btype = property.PropertyType;
if (btype == typeof(bool))
{
var pro = FindSearchProperty(property, entityType);
if (pro != null)
{
query = ApplyComparisonFilter(query, entityType, pro, value, ReturnOperator(property));
}
continue;
}
if (btype == typeof(DateTime) || btype == typeof(DateTime?))
{
if (property.GetCustomAttributes<PasteDaterangeAttribute>()?.Any() == true)
{
var find = property.GetCustomAttributes<PasteDaterangeAttribute>().FirstOrDefault();
if (find != null && find != default)
{
var edate_pro = find.Args2;
var sdate_str = property.GetValue(input);
var end_property = properties.Where(x => x.Name == edate_pro).FirstOrDefault();
if (end_property != null && end_property != default)
{
if (DateTime.TryParse(sdate_str.ToString(), out var sdate))
{
var edate_str = end_property.GetValue(input);
if (DateTime.TryParse(edate_str.ToString(), out var edate))
{
var pro = FindSearchProperty(property, entityType);
if (pro != null)
{
query = ApplyComparisonFilter(query, entityType, pro, sdate, "LessThanOrEqual");
query = ApplyComparisonFilter(query, entityType, pro, edate, "GreaterThanOrEqual");
}
else
{
Logger.LogInformation("没有在字段:{0} 中找到对应的字段 是否配置了PasteField特性 !", property.Name);
}
}
}
}
ignors.Add(edate_pro);
}
}
continue;
}
if (btype == typeof(string))
{
var pro = FindSearchProperty(property, entityType);
if (pro != null)
{
query = ApplyComparisonFilter(query, entityType, pro, value.ToString(), ReturnOperator(property));
}
continue;
}
////是否有PasteFieldAttribute特性 比如 user_id实际为UserId
//var target_property = property.Name;
//if (property.GetCustomAttributes<PasteFieldAttribute>()?.Any() == true)
//{
// var find = property.GetCustomAttributes<PasteFieldAttribute>().FirstOrDefault();
// if (find != null && find != default)
// {
// target_property = find.Args1;
// }
//}
//// 检查实体是否有对应的属性
//var entityProperty = entityType.GetProperty(target_property);
//if (entityProperty != null && entityProperty.PropertyType == property.PropertyType)
//{
// // 动态构建表达式: x => x.Property == value
// query = ApplyEqualsFilter(query, entityType, entityProperty, value);
//}
}
return query;
}
/// <summary>
/// 获取运算符
/// </summary>
/// <param name="property"></param>
/// <returns></returns>
private string ReturnOperator(PropertyInfo property)
{
var finds = property.GetCustomAttributes<PasteFieldAttribute>();
if (finds?.Any() == true)
{
var find = finds.FirstOrDefault();
if (find != null && find != default)
{
if (!String.IsNullOrEmpty(find.Args2))
{
return find.Args2;
}
}
}
return "Equals";
}
/// <summary>
///
/// </summary>
/// <param name="property"></param>
/// <param name="entityType"></param>
/// <returns></returns>
private PropertyInfo FindSearchProperty(PropertyInfo property, Type entityType)
{
var target_property = property.Name;
if (property.GetCustomAttributes<PasteFieldAttribute>()?.Any() == true)
{
var find = property.GetCustomAttributes<PasteFieldAttribute>().FirstOrDefault();
if (find != null && find != default)
{
target_property = find.Args1;
}
}
// 检查实体是否有对应的属性
var entityProperty = entityType.GetProperty(target_property);
if (entityProperty != null)
{
//&& entityProperty.PropertyType == property.PropertyType
// 动态构建表达式: x => x.Property == value
//query = ApplyEqualsFilter(query, entityType, entityProperty, value);
return entityProperty;
}
else
{
//Console.WriteLine($"没有找到这个字段:{target_property} !");
}
return null;
}
/// <summary>
/// 判断值是否为类型的默认值
/// </summary>
/// <param name="value"></param>
/// <param name="type"></param>
/// <returns></returns>
private bool IsDefaultValue(object value, Type type)
{
if (value == null)
return true;
// 值类型处理
if (type.IsValueType)
{
// 可空类型
if (Nullable.GetUnderlyingType(type) != null)
{
if (value == null)
return true;
// 获取基础类型
type = Nullable.GetUnderlyingType(type);
}
// 创建默认值实例
object defaultValue = Activator.CreateInstance(type);
// 比较值
return value.Equals(defaultValue);
}
// 引用类型处理
if (type == typeof(string))
{
return string.IsNullOrEmpty(value.ToString());
}
return false;
}
/// <summary>
/// 应用等于过滤条件的方法
/// 简单应用过滤查询
/// </summary>
/// <param name="query"></param>
/// <param name="entityType"></param>
/// <param name="property"></param>
/// <param name="value"></param>
/// <returns></returns>
private IQueryable ApplyEqualsFilter(IQueryable query, Type entityType, PropertyInfo property, object value)
{
var parameter = Expression.Parameter(entityType, "x");
var propertyAccess = Expression.Property(parameter, property.Name);
var constant = Expression.Constant(value);
var equalExpression = Expression.Equal(propertyAccess, constant);
var lambda = Expression.Lambda(equalExpression, parameter);
var whereCallExpression = Expression.Call(
typeof(Queryable),
"Where",
new Type[] { entityType },
query.Expression,
lambda);
return query.Provider.CreateQuery(whereCallExpression);
}
/// <summary>
/// 应用比较过滤条件(支持多种操作符)
/// 请勿删除,是上面这个的扩展
/// </summary>
private IQueryable ApplyComparisonFilter(IQueryable query, Type entityType, PropertyInfo property, object value, string operatorName = "Equals")
{
var parameter = Expression.Parameter(entityType, "x");
var propertyAccess = Expression.Property(parameter, property.Name);
var constant = Expression.Constant(value, property.PropertyType);
// 根据操作符名称构建表达式
Expression comparisonExpression;
switch (operatorName)
{
case "Equals":
comparisonExpression = Expression.Equal(propertyAccess, constant);
break;
case "NotEquals":
comparisonExpression = Expression.NotEqual(propertyAccess, constant);
break;
case "GreaterThan":
comparisonExpression = Expression.GreaterThan(propertyAccess, constant);
break;
case "GreaterThanOrEqual":
comparisonExpression = Expression.GreaterThanOrEqual(propertyAccess, constant);
break;
case "LessThan":
comparisonExpression = Expression.LessThan(propertyAccess, constant);
break;
case "LessThanOrEqual":
comparisonExpression = Expression.LessThanOrEqual(propertyAccess, constant);
break;
case "Contains":
// 处理字符串Contains
if (property.PropertyType == typeof(string))
{
var containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) });
comparisonExpression = Expression.Call(propertyAccess, containsMethod, constant);
}
else
{
// 处理集合Contains(如List<T>.Contains)
var containsMethod = property.PropertyType.GetMethod("Contains", new[] { property.PropertyType.GenericTypeArguments[0] });
comparisonExpression = Expression.Call(propertyAccess, containsMethod, constant);
}
break;
default:
throw new ArgumentException($"不支持的操作符: {operatorName}");
}
var lambda = Expression.Lambda(comparisonExpression, parameter);
var whereCallExpression = Expression.Call(
typeof(Queryable),
"Where",
new Type[] { entityType },
query.Expression,
lambda);
return query.Provider.CreateQuery(whereCallExpression);
}
///// <summary>
///// 应用搜索关键词的方法
///// </summary>
///// <param name="query"></param>
///// <param name="entityType"></param>
///// <param name="keyword"></param>
///// <returns></returns>
//private IQueryable ApplySearchKeyword(IQueryable query, Type entityType, string keyword)
//{
// if (String.IsNullOrEmpty(keyword))
// {
// return query;
// }
// // 获取所有字符串类型的属性
// var stringProperties = entityType.GetProperties()
// .Where(p => p.PropertyType == typeof(string))
// .ToList();
// if (!stringProperties.Any())
// return query;
// // 构建 OR 表达式: x => x.Prop1.Contains(keyword) || x.Prop2.Contains(keyword) || ...
// var parameter = Expression.Parameter(entityType, "x");
// Expression orExpression = null;
// var containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) });
// foreach (var property in stringProperties)
// {
// var propertyAccess = Expression.Property(parameter, property.Name);
// var constant = Expression.Constant(keyword);
// var containsExpression = Expression.Call(propertyAccess, containsMethod, constant);
// if (orExpression == null)
// orExpression = containsExpression;
// else
// orExpression = Expression.OrElse(orExpression, containsExpression);
// }
// if (orExpression != null)
// {
// var lambda = Expression.Lambda(orExpression, parameter);
// var whereCallExpression = Expression.Call(
// typeof(Queryable),
// "Where",
// new Type[] { entityType },
// query.Expression,
// lambda);
// query = query.Provider.CreateQuery(whereCallExpression);
// }
// return query;
//}
/// <summary>
/// 应用关键字搜索
/// </summary>
/// <param name="query"></param>
/// <param name="entityType"></param>
/// <param name="keyword"></param>
/// <returns></returns>
private IQueryable ApplySearchKeyword(IQueryable query, Type entityType, string keyword)
{
// 外部已确保keyword非空,这里仅做防御性校验
if (string.IsNullOrEmpty(keyword))
{
return query;
}
var stringProperties = entityType.GetProperties()
.Where(p => p.PropertyType == typeof(string) && p.GetCustomAttributes<PasteSearchAttribute>()?.Any() == true)
.ToList();
if (!stringProperties.Any())
{
return query;
}
var parameter = Expression.Parameter(entityType, "x");
Expression orExpression = null;
var containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) });
foreach (var property in stringProperties)
{
// 只构建“属性包含关键词”的表达式,不添加任何与关键词为空相关的判断
var propertyAccess = Expression.Property(parameter, property);
var containsExpression = Expression.Call(
propertyAccess,
containsMethod,
Expression.Constant(keyword) // 直接使用关键词“bb”
);
orExpression = orExpression == null
? containsExpression
: Expression.OrElse(orExpression, containsExpression);
}
var lambda = Expression.Lambda(orExpression, parameter);
var whereCall = Expression.Call(
typeof(Queryable),
"Where",
new[] { entityType },
query.Expression,
lambda
);
return query.Provider.CreateQuery(whereCall);
}
/// <summary>
/// 计算 page size orderby
/// </summary>
/// <param name="query"></param>
/// <param name="entityType"></param>
/// <param name="input"></param>
/// <returns></returns>
/// <exception cref="PasteCodeException"></exception>
private IQueryable ApplyPagingAndSorting(IQueryable query, Type entityType, InputSearchBase input)
{
// 获取Id属性信息
var idProperty = entityType.GetProperty("Id");
if (idProperty == null)
{
throw new PasteCodeException($"实体 {entityType.Name} 不包含Id属性", 500);
}
// 构建排序表达式
var parameter = Expression.Parameter(entityType, "x");
var propertyAccess = Expression.Property(parameter, idProperty);
var lambda = Expression.Lambda(propertyAccess, parameter);
// 使用正确的属性类型作为排序键类型
var orderByDescendingCall = Expression.Call(
typeof(Queryable),
"OrderByDescending",
new Type[] { entityType, idProperty.PropertyType }, // 使用实际的属性类型
query.Expression,
lambda);
query = query.Provider.CreateQuery(orderByDescendingCall);
// 应用分页
query = query.Skip((input.page - 1) * input.size).Take(input.size);
return query;
}
/// <summary>
/// 上面的补充,用于其他查询
/// </summary>
/// <param name="query"></param>
/// <param name="entityType"></param>
/// <param name="searchType"></param>
/// <param name="input"></param>
/// <returns></returns>
/// <exception cref="PasteCodeException"></exception>
private IQueryable ApplyPagingAndSorting(IQueryable query, Type entityType, Type searchType, Object input)
{
// 获取Id属性信息
var idProperty = entityType.GetProperty("Id");
if (idProperty == null)
{
throw new PasteCodeException($"实体 {entityType.Name} 不包含Id属性", 500);
}
var page = 1;
var size = 20;
var orderby = String.Empty;
foreach (var ii in searchType.GetProperties())
{
if (string.Equals(ii.Name, "page", StringComparison.CurrentCultureIgnoreCase))
{
var _page = ii.GetValue(input);
int.TryParse(_page.ToString(), out page);
continue;
}
if (string.Equals(ii.Name, "size", StringComparison.CurrentCultureIgnoreCase))
{
var _page = ii.GetValue(input);
int.TryParse(_page.ToString(), out size);
continue;
}
if (string.Equals(ii.Name, "orderby", StringComparison.CurrentCultureIgnoreCase))
{
var _page = ii.GetValue(input);
orderby = _page.ToString().Trim();
continue;
}
}
if (!String.IsNullOrEmpty(orderby))
{
query = query.OrderBy(orderby);
}
else
{
// 构建排序表达式 默认 OrderByDescending(x=>x.Id)
var parameter = Expression.Parameter(entityType, "x");
var propertyAccess = Expression.Property(parameter, idProperty);
var lambda = Expression.Lambda(propertyAccess, parameter);
// 使用正确的属性类型作为排序键类型
var orderByDescendingCall = Expression.Call(
typeof(Queryable),
"OrderByDescending",
new Type[] { entityType, idProperty.PropertyType }, // 使用实际的属性类型
query.Expression,
lambda);
query = query.Provider.CreateQuery(orderByDescendingCall);
}
// 应用分页
query = query.Skip((page - 1) * size).Take(size);
return query;
}
/// <summary>
/// 执行计数查询的方法
/// </summary>
/// <param name="query"></param>
/// <param name="entityType"></param>
/// <returns></returns>
private async Task<int> ExecuteCountQuery(IQueryable query, Type entityType)
{
var countMethod = typeof(Queryable)
.GetMethods()
.First(m => m.Name == "Count" && m.GetParameters().Length == 1)
.MakeGenericMethod(entityType);
return (int)await Task.FromResult(countMethod.Invoke(null, new object[] { query }));
}
/// <summary>
///
/// </summary>
/// <param name="query"></param>
/// <param name="entityType"></param>
/// <returns></returns>
/// <exception cref="PasteCodeException"></exception>
private async Task<List<object>> ExecuteQuery(IQueryable query, Type entityType)
{
try
{
// 获取 EntityFrameworkQueryableExtensions 类型
var extensionsType = typeof(EntityFrameworkQueryableExtensions);
// 查找 ToListAsync 方法,匹配名称、泛型和参数
var toListMethod = extensionsType.GetMethods(BindingFlags.Static | BindingFlags.Public)
.First(m =>
m.Name == "ToListAsync" &&
m.IsGenericMethodDefinition &&
m.GetParameters().Length == 2 && // 注意:有两个参数(查询和 CancellationToken)
m.GetParameters()[0].ParameterType == typeof(IQueryable<>).MakeGenericType(m.GetGenericArguments()[0]) &&
m.GetParameters()[1].ParameterType == typeof(CancellationToken));
// 构建泛型方法
var genericToListMethod = toListMethod.MakeGenericMethod(entityType);
// 调用方法,传入 CancellationToken.None
var task = (Task)genericToListMethod.Invoke(null, new object[] { query, CancellationToken.None });
await task;
// 获取结果
var resultProperty = task.GetType().GetProperty("Result");
var entities = (IEnumerable)resultProperty.GetValue(task);
return entities.Cast<object>().ToList();
}
catch (Exception ex)
{
throw new PasteCodeException($"执行查询时发生错误: {ex.Message}", 500);
}
}
/// <summary>
/// 映射实体到DTO的方法
/// </summary>
/// <param name="entities"></param>
/// <param name="entityType"></param>
/// <param name="dtoType"></param>
/// <returns></returns>
private List<object> MapEntitiesToDtos(List<object> entities, Type entityType, Type dtoType)
{
var result = new List<object>();
foreach (var entity in entities)
{
var dto = Activator.CreateInstance(dtoType);
// 获取DTO类型的所有可写属性
var dtoProperties = dtoType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => p.CanWrite);
foreach (var dtoProperty in dtoProperties)
{
// 查找实体中对应的属性
var entityProperty = entityType.GetProperty(dtoProperty.Name);
// 如果属性名称和类型都匹配,则复制值
if (entityProperty != null && entityProperty.PropertyType == dtoProperty.PropertyType)
{
var value = entityProperty.GetValue(entity);
dtoProperty.SetValue(dto, value);
}
}
result.Add(dto);
}
return result;
}
// 类型缓存
//private readonly ConcurrentDictionary<string, Type> _typeCache = new ConcurrentDictionary<string, Type>();
#endregion
#region
/// <summary>
/// 动态构建Select表达式,将实体投影到DTO
/// </summary>
private Expression<Func<object, object>> BuildSelectExpressionV1(Type entityType, Type dtoType)
{
var parameter = Expression.Parameter(typeof(object), "x");
var convertedParameter = Expression.Convert(parameter, entityType);
// 创建DTO构造表达式
var dtoNewExpression = Expression.New(dtoType);
// 构建属性赋值绑定
var bindings = new List<MemberBinding>();
foreach (var dtoProperty in dtoType.GetProperties())
{
// 查找实体中对应的属性
var entityProperty = entityType.GetProperty(dtoProperty.Name);
// 如果属性名称和类型都匹配,则添加到绑定
if (entityProperty != null && entityProperty.PropertyType == dtoProperty.PropertyType)
{
var propertyAccess = Expression.Property(convertedParameter, entityProperty);
bindings.Add(Expression.Bind(dtoProperty, propertyAccess));
}
}
// 创建初始化器表达式
var initExpression = Expression.MemberInit(dtoNewExpression, bindings);
// 转换为object类型
var convertExpression = Expression.Convert(initExpression, typeof(object));
// 创建lambda表达式
return Expression.Lambda<Func<object, object>>(convertExpression, parameter);
}
//private LambdaExpression BuildSelectExpression(Type entityType, Type dtoType)
//{
// // 创建参数表达式 (x => ...)
// var parameter = Expression.Parameter(entityType, "x");
// // 创建DTO属性的绑定
// var bindings = dtoType.GetProperties()
// .Where(p => p.CanWrite)
// .Select(p =>
// {
// // 查找实体类型中是否有同名属性
// var entityProperty = entityType.GetProperty(p.Name);
// if (entityProperty != null && entityProperty.CanRead)
// {
// // 创建属性访问表达式 x.Property
// var propertyAccess = Expression.Property(parameter, entityProperty);
// // 如果需要类型转换,添加Convert操作
// if (p.PropertyType != entityProperty.PropertyType)
// {
// propertyAccess = Expression.Convert(propertyAccess, p.PropertyType);
// }
// return Expression.Bind(p, propertyAccess);
// }
// return null;
// })
// .Where(b => b != null)
// .ToList();
// // 创建DTO对象初始化表达式
// var dtoInit = Expression.MemberInit(
// Expression.New(dtoType),
// bindings);
// // 创建lambda表达式 x => new Dto { ... }
// return Expression.Lambda(dtoInit, parameter);
//}
/// <summary>
/// 直接查询并投影为DTO
/// </summary>
private async Task<List<object>> ExecuteProjectionQuery(IQueryable query, Type entityType, Type dtoType)
{
try
{
// 构建Select表达式
var selectExpression = BuildSelectExpression(entityType, dtoType);
// 创建泛型Func类型 (EntityType -> DtoType)
var funcType = typeof(Func<,>).MakeGenericType(entityType, dtoType);
// 创建lambda表达式
var lambda = Expression.Lambda(funcType, selectExpression.Body, selectExpression.Parameters[0]);
// 构建Select方法
var selectMethod = typeof(Queryable).GetMethods()
.First(m => m.Name == "Select" && m.GetParameters().Length == 2)
.MakeGenericMethod(entityType, dtoType);
// 应用Select投影
var projectedQuery = selectMethod.Invoke(null, new object[] { query, lambda });
var toListMethod = typeof(EntityFrameworkQueryableExtensions).GetMethods()
.First(m => m.Name == "ToListAsync" && m.GetParameters().Length == 2)
.MakeGenericMethod(typeof(object));
//Console.WriteLine("line 1640");
var task = (Task)toListMethod.Invoke(null, new object[] { projectedQuery, CancellationToken.None });
await task;
//Console.WriteLine("line 1643");
var resultProperty = task.GetType().GetProperty("Result");
return (List<object>)resultProperty.GetValue(task);
}
catch (Exception ex)
{
throw new PasteCodeException($"执行投影查询时发生错误: {ex.Message}", 500);
}
}
private LambdaExpression BuildSelectExpression(Type entityType, Type dtoType)
{
// 创建参数表达式 (x => ...)
var parameter = Expression.Parameter(entityType, "x");
// 创建DTO属性的绑定
var bindings = dtoType.GetProperties()
.Where(p => p.CanWrite)
.Select(p =>
{
// 查找实体类型中是否有同名属性
var entityProperty = entityType.GetProperty(p.Name);
if (entityProperty != null && entityProperty.CanRead)
{
// 创建属性访问表达式 x.Property
var propertyAccess = Expression.Property(parameter, entityProperty);
// 如果需要类型转换,添加Convert操作
//if (p.PropertyType != entityProperty.PropertyType)
//{
// propertyAccess = Expression.Convert(propertyAccess, p.PropertyType);
//}
return Expression.Bind(p, propertyAccess);
}
return null;
})
.Where(b => b != null)
.ToList();
// 创建DTO对象初始化表达式
var dtoInit = Expression.MemberInit(
Expression.New(dtoType),
bindings);
// 创建lambda表达式 x => new Dto { ... }
return Expression.Lambda(dtoInit, parameter);
}
/// <summary>
/// 构建ID查询条件
/// </summary>
private IQueryable BuildIdQuery(Type entityType, HashSet<object> ids)
{
// 获取DbSet
var dbSetMethod = _dbContext.GetType()
.GetMethods()
.First(m => m.Name == "Set" && m.IsGenericMethod);
var genericDbSetMethod = dbSetMethod.MakeGenericMethod(entityType);
var dbSet = genericDbSetMethod.Invoke(_dbContext, null);
var queryable = (IQueryable)dbSet;
//if (ids.Count == 0)
//{
// return queryable.Where(x => false); // 返回空结果
//}
// 动态构建ID查询表达式: x => ids.Contains(x.Id)
var parameter = Expression.Parameter(entityType, "x");
var idProperty = entityType.GetProperty("Id");
var propertyAccess = Expression.Property(parameter, idProperty);
// 创建包含所有ID的常量表达式
var method = typeof(Enumerable).GetMethods()
.First(m => m.Name == "Contains" && m.GetParameters().Length == 2)
.MakeGenericMethod(idProperty.PropertyType);
var constant = Expression.Constant(ids);
var containsExpression = Expression.Call(method, constant, propertyAccess);
var lambda = Expression.Lambda(containsExpression, parameter);
var whereCallExpression = Expression.Call(
typeof(Queryable),
"Where",
new Type[] { entityType },
queryable.Expression,
lambda);
return queryable.Provider.CreateQuery(whereCallExpression);
}
#endregion
/// <summary>
///
/// </summary>
/// <param name="entityType"></param>
/// <returns></returns>
private object GetDbSet(Type entityType)
{
var dbSetMethod = _dbContext.GetType()
.GetMethods()
.First(m => m.Name == "Set" && m.IsGenericMethod);
return dbSetMethod.MakeGenericMethod(entityType).Invoke(_dbContext, null);
}
/// <summary>
///
/// </summary>
/// <param name="obj"></param>
/// <param name="propertyName"></param>
/// <returns></returns>
private object GetPropertyValue(object obj, string propertyName)
{
var property = obj.GetType().GetProperty(propertyName);
return property?.GetValue(obj);
}
/// <summary>
///
/// </summary>
/// <param name="obj"></param>
/// <param name="propertyName"></param>
/// <param name="value"></param>
private void SetPropertyValue(object obj, string propertyName, object value)
{
var property = obj.GetType().GetProperty(propertyName);
if (property != null && property.CanWrite)
{
// 处理类型转换
if (value != null && property.PropertyType != value.GetType())
{
value = Convert.ChangeType(value, property.PropertyType);
}
property.SetValue(obj, value);
}
}
/// <summary>
///
/// </summary>
/// <param name="source"></param>
/// <param name="destination"></param>
/// <param name="sourceType"></param>
/// <param name="destinationType"></param>
private void MapProperties(object source, object destination, Type sourceType, Type destinationType)
{
// 获取源类型的所有可读属性
var sourceProperties = sourceType.GetProperties()
.Where(p => p.CanRead)
.ToList();
// 获取目标类型的所有可写属性
var destinationProperties = destinationType.GetProperties()
.Where(p => p.CanWrite)
.ToList();
// 映射相同名称和类型的属性
foreach (var sourceProperty in sourceProperties)
{
var destinationProperty = destinationProperties
.FirstOrDefault(p => p.Name == sourceProperty.Name && p.PropertyType == sourceProperty.PropertyType);
if (destinationProperty != null)
{
var value = sourceProperty.GetValue(source);
destinationProperty.SetValue(destination, value);
}
}
}
#endregion
}
}
有几个注意要点
1.所有的Dto查询等要放在同一个项目,
也就是XXX.Application.Contracts下面,
关键点和InputSearchBase放一个项目,
你看代码会发现查找反射的时候是基于这个查找的
2.如果你的项目没有UserInfo这个Entity,
则替换成一个基础的就行,原理和上面的一样,
为了在程序集按照名称查找!
3.如果你某一个Entity要实现自己的逻辑,
你按照代码生成即可,因为默认的路由权重比较高,会覆盖这个!
4.基于上面的信息,XXX.Domain/template/config.json下面多了一个配置
“create_appservice”: true,//是否创建对应的AppService
5.恭喜你,你得项目管理端非常干净了,真的非常干净,可以做到管理端页面不超过10个,而管理端的接口不多于2个!!!
当然了,如果你有比较特殊的需求除外哈!
6.记住PasteForm的宗旨是,Dto驱动管理端,所以至于业务端等是原来怎么写就怎么写!
甚至我觉得用户端的有些表单也可以用这个思路来开发!
因为他高效,简洁!特备适合频繁变更需求的,所见即所得!