相信大家在用EF对数据库数据进行操作的时候,会感觉有一些实体之间的来回转换非常的繁琐,一个表如果有几十个字段,有时候要逐个复制,显得代码累赘,繁琐,操作性能也不是很好,特别是遇到二次更改时候,更是难受,一个字段一个字段找,改。!!下面我就说几个关于语法优化的写法。
一、我先新建两个类,等下所有操作都是基于这两个类进行
这两个类只是一个示例,数据库中新建的表做的示范!!!,示范!!!
1、第一个是数据库中一个表的全部实体
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Models
{
//数据库实体类
[Table("Users")]
public class Users
{
/// <summary>
/// id
/// </summary>
[Key]
public Guid Uid { get; set; } = Guid.Empty;
/// <summary>
/// 名称
/// </summary>
public string UName { get; set; } = string.Empty;
/// <summary>
/// 密码
/// </summary>
public string Pwd { get; set; } = string.Empty;
}
}
2、第二个是我等下用来转换的,将数据库实体批量转换为网站实体,可隐藏敏感数据,属性名称和类型需一一对应
namespace RedisCeShi.Model
{
//我想让他们看到的,或者说是,我想让他们操作的数据
public class ReqUsers
{
public string UName { get; set; }
public string Pwd { get; set; }
}
}
二、代码中常见的写法(优化前!!!没有优化的时候)
我这里就直接给大家复制全部了,大家都能看得懂,不在一 一举例了
/// <summary>
/// 测试查询全部
/// </summary>
/// <returns></returns>
[HttpGet]
public async Task<ActionResult> GetUsers()
{
var list = await _db.Users.ToListAsync();
return Ok(list);
}
/// <summary>
/// 添加测试
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
[HttpPost]
public async Task<ActionResult> InsertList(ReqUsers user)
{
try
{
Users model = new Users();
model.Uid = 0;
model.UName = user.UName;
model.Pwd = user.Pwd;
await _db.Users.AddAsync(model);
int num = await _db.SaveChangesAsync();
if (num > 0)
{
return Ok("添加成功");
}
return BadRequest("添加失败");
}
catch (Exception ex)
{
return BadRequest($"添加失败:{ex.Message}");
}
}
/// <summary>
/// 测试编辑
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
[HttpPost]
public async Task<ActionResult> UpdataModel(Users user)
{
var model = await _db.Users.AsNoTracking().FirstOrDefaultAsync(m => m.Uid == user.Uid);
if (model is not null)
{
model.UName = user.UName;
model.Pwd = user.Pwd;
var entry = _db.Entry(model);//跟踪实体的属性更改以及将更改保存回数据库
entry.State = EntityState.Modified;// 应将其状态标记为已修改
if (await _db.SaveChangesAsync() > 0)
{
return Ok("编辑成功");
}
}
return BadRequest("编辑失败");
}
在语法没有优化的时候,我这个表只有三个字段,查起来还是代码简洁,比较轻便的,但是如果字段多的时候,我们就会写很多实体字段附加,很没有效率。所以我们就要另找方法。⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇
三、创建辅助类(优化方法封装)
1、生成表达式目录树 泛型缓存(实现批量实体转换)
using System.Linq.Expressions;
using System.Reflection;
namespace RedisCeShi.Common
{
/// <summary>
/// 生成表达式目录树 泛型缓存
/// </summary>
/// <typeparam name="TIn"></typeparam>
/// <typeparam name="TOut"></typeparam>
public class ExpressionGenericMapper<TIn, TOut>//Mapper`2
{
private static Func<TIn, TOut> _FUNC = null;
private static Func<TIn, TOut, TOut> _FUNC2 = null;
public static TOut Trans(TIn t)
{
return _FUNC(t);
}
/// <summary>
/// 批量实体转换
/// </summary>
/// <param name="list">被转换的实体List</param>
/// <returns></returns>
public static List<TOut> TransList(List<TIn> list)
{
List<TOut> outs = new List<TOut>();
foreach (var item in list)
{
outs.Add(Trans(item));
}
return outs;
}
/// <summary>
/// 把TOut 按照TIn来更新
/// </summary>
/// <param name="news"></param>
/// <param name="old"></param>
/// <returns></returns>
public static TOut Modify(TIn news, TOut old)
{
return _FUNC2(news, old);
}
static ExpressionGenericMapper()
{
Type stringType = typeof(string);
#region 创建构造对象的委托
{
ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "m");
List<MemberBinding> memberBindingList = new List<MemberBinding>();
foreach (var item in typeof(TOut).GetProperties())
{
var type = typeof(TIn);
if (item.PropertyType.IsClass && item.PropertyType != stringType)//排除String以外的引用类型,不进行赋值
{
continue;
}
var prop = type.GetProperty(item.GetMappingName());//从Out类找映射的字段名
if (prop != null)//如果TIn里能找到该属性
{
MemberExpression property = Expression.Property(parameterExpression, prop);
MemberBinding memberBinding = Expression.Bind(item, property);
memberBindingList.Add(memberBinding);
}
}
foreach (var item in typeof(TOut).GetFields())
{
var type = typeof(TIn);
if (item.FieldType.IsClass && item.FieldType != stringType)//排除String以外的引用类型,不进行赋值
{
continue;
}
var prop = type.GetField(item.GetMappingName());//从Out类找映射的字段名
if (prop != null)
{
MemberExpression property = Expression.Field(parameterExpression, prop);
MemberBinding memberBinding = Expression.Bind(item, property);
memberBindingList.Add(memberBinding);
}
}
MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray());
Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, new ParameterExpression[]
{
parameterExpression
});
_FUNC = lambda.Compile();//拼装是一次性的
}
#endregion
#region 修改对象的委托
{
ParameterExpression oExpression = Expression.Parameter(typeof(TOut), "o");
ParameterExpression nExpression = Expression.Parameter(typeof(TIn), "n");
List<MemberBinding> memberBindingList = new List<MemberBinding>();
foreach (var item in typeof(TOut).GetProperties())
{
var type = typeof(TIn);
if (item.PropertyType.IsClass && item.PropertyType != stringType)//排除String以外的引用类型,不进行赋值
{
continue;
}
var prop = type.GetProperty(item.GetMappingName());
if (prop != null)
{
MemberExpression property = Expression.Property(nExpression, prop);
MemberBinding memberBinding = Expression.Bind(item, property);
memberBindingList.Add(memberBinding);
}
else
{
var propOld = typeof(TOut).GetProperty(item.Name);
MemberExpression property = Expression.Property(oExpression, propOld);
MemberBinding memberBinding = Expression.Bind(item, property);
memberBindingList.Add(memberBinding);
}
}
foreach (var item in typeof(TOut).GetFields())
{
var type = typeof(TIn);
if (item.FieldType.IsClass && item.FieldType != stringType)//排除String以外的引用类型,不进行赋值
{
continue;
}
var prop = type.GetField(item.GetMappingName());
if (prop != null)
{
MemberExpression property = Expression.Field(nExpression, prop);
MemberBinding memberBinding = Expression.Bind(item, property);
memberBindingList.Add(memberBinding);
}
else
{
var propOld = typeof(TOut).GetField(item.Name);
MemberExpression property = Expression.Field(oExpression, propOld);
MemberBinding memberBinding = Expression.Bind(item, property);
memberBindingList.Add(memberBinding);
}
}
MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray());
Expression<Func<TIn, TOut, TOut>> lambda = Expression.Lambda<Func<TIn, TOut, TOut>>(memberInitExpression, new ParameterExpression[]
{
nExpression, oExpression
});
_FUNC2 = lambda.Compile();//拼装是一次性的
}
#endregion
}
}
public static class ExpressionGenericMapperExtend
{
public static string GetMappingName(this PropertyInfo prop)
{
if (prop.IsDefined(typeof(MappingAttribute), true))
{
MappingAttribute display = prop.GetCustomAttribute(typeof(MappingAttribute)) as MappingAttribute;
return display.MappingName;
}
return prop.Name;
}
public static string GetMappingName(this FieldInfo prop)
{
if (prop.IsDefined(typeof(MappingAttribute), true))
{
MappingAttribute display = prop.GetCustomAttribute(typeof(MappingAttribute)) as MappingAttribute;
return display.MappingName;
}
return prop.Name;
}
}
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class MappingAttribute : Attribute
{
public string MappingName;
public MappingAttribute(string _MappingName)
{
this.MappingName = _MappingName;
}
}
}
2、对象值比较(修改时比较是否修改过字段)
namespace RedisCeShi.Common
{
/// <summary>
/// 对象值比较
/// </summary>
public static class ObjectEqualsExtend
{
/// <summary>
/// 对比两个对象属性值扩展方法,只针对一个对象下的“属性值”
/// 仅支持常规数据类型比较,对象下的属性是:(如String int Guid Double DateTime decimal bool 类型)
/// </summary>
/// <typeparam name="ObjectA">比较对象A泛型类型</typeparam>
/// <typeparam name="ObjectB">比较对象B泛型类型</typeparam>
/// <param name="objectA">对象A</param>
/// <param name="objectB">对象B</param>
/// <returns></returns>
public static bool IsEqualsValue<ObjectA, ObjectB>(this ObjectA objectA, ObjectB objectB)
where ObjectB : class
where ObjectA : class
{
var propA = objectA.GetType().GetProperties();
var typeB = objectB.GetType();
bool flag = true;
foreach (var item in propA)
{
var propB = typeB.GetProperty(item.Name);//从B里找到属性
if (propB != null)
{
if (item.PropertyType.Name == "Decimal" && (decimal)item.GetValue(objectA) != (decimal)propB.GetValue(objectB))
{
flag = false;//该返回比较结果不相等了
break;//退出循环
}
else if (item.PropertyType.Name != "Decimal" && item.GetValue(objectA).ToString() != propB.GetValue(objectB).ToString())//如果属性值不相等
{
flag = false;//该返回比较结果不相等了
break;//退出循环
}
}
}
return flag;
}
}
}
四、优化后代码
代码里所运用的实体均是我上面写的两个示例实体Users,ReqUsers,方便大家查看!!!!
1、查询
/// <summary>
/// 测试
/// </summary>
/// <param name="pageIndex"></param>
/// <param name="pageSize"></param>
/// <param name="Name"></param>
/// <returns></returns>
[HttpGet]
public async Task<ActionResult> GetList(int pageIndex = 1, int pageSize = 10)
{
//验证数据,返回错误提示
if (pageIndex < 1 || pageSize < 1)
{
return BadRequest("参数有误");
}
var list = await _db.Users.Where(m => m.Uid != null).OrderByDescending(m => m.Uid).Skip((pageIndex - 1) * pageSize).Take(pageSize).ToListAsync();
var recordCount = await _db.Users.Where(m => m.Uid != null).CountAsync();
//Users里面又Id但是我不想让别人看见,我的ReqUsers里面没有Id这个字段,所以用这个转换一下就可以隐藏敏感字段
var result = ExpressionGenericMapper<Users, ReqUsers>.TransList(list);
return Ok(result);
}
2、添加
/// 添加测试
/// <summary>
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
[HttpPost]
public async Task<ActionResult> InsertList(ReqUsers user)
{
try
{
//添加时候,我们也可以隐藏敏感字段在后台自己设置,这里我只让他填写ReqUsers里面的字段,然后通过下述方法转换成Users实体,自己在后端定义敏感字段
var model = ExpressionGenericMapper<ReqUsers, Users>.Trans(user);
model.Uid = Guid.NewGuid();
await _db.Users.AddAsync(model);
int num = await _db.SaveChangesAsync();
if (num > 0)
{
return Ok("添加成功");
}
return Ok("添加失败");
}
catch (Exception ex)
{
return Forbid($"添加失败:{ex.Message}");
}
}
3、编辑修改
/// <summary>
/// 测试编辑
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
[HttpPost]
public async Task<ActionResult> UpdataModel(Users user)
{
try
{
//AsNoTracking() 是 EF Core 中的一个方法,用于查询实体时指示 EF Core 不要跟踪实体的更改。使用 AsNoTracking() 方法可以提高查询性能,并减少内存消耗。
var model = await _db.Users.AsNoTracking().FirstOrDefaultAsync(m => m.Uid == user.Uid);
//验证实体是否有修改,有修改执行if没修改直接返回成功
if (!user.IsEqualsValue(model))
{
//表达式目录是进行实体转换
var modify = ExpressionGenericMapper<Users, Users>.Modify(user, model);
//使用 EntityEntry 对象来获取或设置实体的状态、属性值和原始值,以及跟踪实体属性的更改。
var entry = _db.Entry(modify);
//将 modify 实体附加到上下文中的 Users 实体集合
_db.Set<Users>().Attach(modify);
//其状态标记为已修改。
entry.State = EntityState.Modified;
int num = await _db.SaveChangesAsync();
if (num > 0)
{
return Ok("编辑成功");
}
}
else
{
return Ok("编辑成功");
}
return BadRequest("编辑失败");
}
catch (Exception e)
{
return BadRequest("内部错误" + e.Message);
}
}