用 API 开发的人都知道, 常用的后台接收参数就是建个 DTO, 然后前台把这个 DTO 传过来. 后台再更新, 例如如下例子:
- public async Task<IActionResult> PutModel(DTO model)
- {
- _context.Entry(model).State = EntityState.Modified;
- var result = await _context.SaveChangesAsync().ConfigureAwait(false);
- return OK("修改实体成功!");
- }
这样做后台是很方便, 可是 EF 全部更新效率实在太低, 而且没必要, 而且前台就需要把所有的 DTO 对象都传过来, 如果有不想要修改的怎么办? 好吧. 用 EF 的
- _context.Entry(model).Property(m => m.Field1).IsModified = false;
- _context.Entry(model).Property(m => m.Field2).IsModified = false;
如果一个表中有 50 个字段, 有 20 个需要更新, 30 个不需要更新, 难道写 30 个_context.Entry(model).Property(m => m.Field1).IsModified = false; 这样的语句? 或者写 20 个_context.Entry(model).Property(m => m.Field2).IsModified =true; 这样的语句? 这样开发效率实在太低.. 好吧. 还有方法 2, 针对每个表需要修改的部分都建不同的 DTO, 再用 model.Field1=DTO.Field1, 这要去对每个修改的对象都赋值再更改, 如果有一百张表呢? 那岂不是要建一百个 DTO,, 我的天, 那多累, 程序猿无疑是最懒的, 如果换作你, 你愿意花时间建 100 个 DTO 吗? 换作我是的话, 我肯定不愿意. 有没有更捷径的方法呢? 肯定有, 那就是用动态传参 [dynamic], 这样后台就不需要针对每个要修改的表都建 DTO, 前台也不需要把表所有的字段都传过来, 只需要传页面表单有的字段, 也就是修改过的字段, 灵活性大大提高, 而且提高了开发效率. 好了, 不废话了. 开始正题.
由于 dynamic 不支持跨域, 首先要解决跨域问题, 在 Startup.cs 文件里添加如下代码
- public void ConfigureServices(IServiceCollection services)
- {
- // 跨域设置
- services.AddCors(options =>
- {
- options.AddPolicy("allow_all", builder =>
- {
- builder.AllowAnyMethod().AllowAnyHeader().AllowAnyOrigin();// 允许所有域名访问
- //builder.WithOrigins("http://localhost:8088").AllowAnyHeader();// 允许指定域名访问
- });
- });
- }
- public void Configure(IApplicationBuilder App, IWebHostEnvironment env, IHttpContextAccessor httpContextAccessor)
- {
- App.UseCors("allow_all");
- }
跨域问题解决之后, 添加动态修改数据库的方法.
- using Microsoft.AspNetCore.Mvc;
- using Microsoft.EntityFrameworkCore;
- using Newtonsoft.JSON;
- using System;
- using System.Collections.Generic;
- using System.ComponentModel.DataAnnotations;
- using System.Linq;
- using System.Reflection;
- namespace ASP.NET.Core.EF
- {
- /// <summary > 页面基类 </summary>
- public class PageBase : ControllerBase
- {
- /// <summary > 数据连接 </summary>
- protected readonly DBContext _context;// 方式 1
- /// <summary > 构造函数 初始化数据库连接 </summary>
- /// <param name="context"> 数据连接 </param>
- public PageBase(DBContext context)
- {
- _context = context;
- }
- /// <summary>
- /// 更新指定实体
- /// </summary>
- /// <typeparam name="T"> 数据表实体 Model 模型 </typeparam>
- /// <param name="modelNew"> 动态 JSON 数据 </param>
- protected virtual void Update<T>(dynamic modelNew)
- {
- // 序列化动态 JSON 为字符串
- string JSON = modelNew.ToString();
- // 反序列化为数据表中的实体对象
- T model = JsonConvert.DeserializeObject<T>(JSON);
- // 把状态全部变为不可更改
- _context.Entry(model).State = EntityState.Unchanged;
- // 反序列化为动态对象中的属性
- var jsonModel = JsonConvert.DeserializeObject<dynamic>(JSON);
- // 定义一个 List 来添加属性
- List<string> listName = new List<string>();
- // 动态添加要修改的字段
- foreach (PropertyInfo info in model.GetType().GetProperties())
- {
- // 如果 EF 表中有实体对象, 则排除, 否则更新会报错, 保留枚举
- if ((info.PropertyType.IsClass && info.PropertyType == typeof(String)) || info.PropertyType.IsClass == false)
- {
- // 解决大小写问题
- foreach (var property in jsonModel)
- {
- if (info.Name.ToLower().Trim() == property.Name.ToLower().Trim())
- {
- listName.Add(info.Name);
- }
- }
- }
- }
- // 转换要修改的字段为数组
- string[] ProNames = string.Join(",", listName).Split(",");
- // 寻找主键
- PropertyInfo pkProp = typeof(T).GetProperties().Where(p => p.GetCustomAttributes(typeof(KeyAttribute), false).Length> 0).FirstOrDefault();
- // 遍历修改, 并排除主键
- foreach (string Name in ProNames)
- {
- if (Name.ToLower() != pkProp.Name.ToLower())
- {
- _context.Entry(model).Property(Name).IsModified = true;
- }
- }
- //return db.SaveChanges();
- }
- /// <summary>
- /// 更新指定实体, 不更新指定字段
- /// </summary>
- /// <typeparam name="T"> 数据表实体 Model 模型 </typeparam>
- /// <param name="modelNew"> 动态 JSON 数据 </param>
- /// <param name="fieldProNames"> 不更新的字段列表数组 </param>
- protected virtual void Update<T>(dynamic modelNew, string fieldProNames)
- {
- // 序列化动态 JSON 为字符串
- string JSON = modelNew.ToString();
- // 反序列化为数据表中的实体对象
- T model = JsonConvert.DeserializeObject<T>(JSON);
- // 把状态全部变为不可更改
- _context.Entry(model).State = EntityState.Unchanged;
- // 反序列化为动态对象中的属性
- var jsonModel = JsonConvert.DeserializeObject<dynamic>(JSON);
- // 定义一个 List 来添加属性
- List<string> listName = new List<string>();
- // 动态添加要修改的字段
- foreach (PropertyInfo info in model.GetType().GetProperties())
- {
- // 如果 EF 表中有实体对象, 则排除, 否则更新会报错, 保留枚举
- if ((info.PropertyType.IsClass && info.PropertyType == typeof(String)) || info.PropertyType.IsClass == false)
- {
- // 解决大小写问题
- foreach (var property in jsonModel)
- {
- if (info.Name.ToLower().Trim() == property.Name.ToLower().Trim())
- {
- listName.Add(info.Name);
- }
- }
- }
- }
- // 转换要修改的字段为数组
- string[] ProNames = string.Join(",", listName).Split(",");
- // 寻找主键
- PropertyInfo pkProp = typeof(T).GetProperties().Where(p => p.GetCustomAttributes(typeof(KeyAttribute), false).Length> 0).FirstOrDefault();
- // 遍历修改, 并排除主键
- foreach (string Name in ProNames)
- {
- if (Name.ToLower() != pkProp.Name.ToLower() && !fieldProNames.Split(",").Select(n => n.ToLower()).Contains(Name.ToLower()))
- {
- _context.Entry(model).Property(Name).IsModified = true;
- }
- }
- //return db.SaveChanges();
- }
- /// <summary>
- /// 更新指定实体, 不更新指定字段, 如果每个表中有相同不更新的字段, 可以这样写
- /// </summary>
- /// <typeparam name="T"> 数据表实体 Model 模型 </typeparam>
- /// <param name="modelNew"> 动态 JSON 数据 </param>
- protected virtual void UpdateSpecify<T>(dynamic modelNew)
- {
- // 序列化动态 JSON 为字符串
- string JSON = modelNew.ToString();
- // 反序列化为数据表中的实体对象
- T model = JsonConvert.DeserializeObject<T>(JSON);
- // 把状态全部变为不可更改
- _context.Entry(model).State = EntityState.Unchanged;
- // 反序列化为动态对象中的属性
- var jsonModel = JsonConvert.DeserializeObject<dynamic>(JSON);
- // 定义一个 List 来添加属性
- List<string> listName = new List<string>();
- // 定义不需要更新的字段
- string fieldProNames = "field1,field2,field3,CreateDate,Creator,IsDel,Updator,UpdateDate";
- // 动态添加要修改的字段
- foreach (PropertyInfo info in model.GetType().GetProperties())
- {
- // 如果 EF 表中有实体对象, 则排除, 否则更新会报错, 保留枚举
- if ((info.PropertyType.IsClass && info.PropertyType == typeof(String)) || info.PropertyType.IsClass == false)
- {
- // 解决大小写问题
- foreach (var property in jsonModel && !fieldProNames.Split(",").Select(n => n.ToLower()).Contains(info.Name.ToLower()))
- {
- if (info.Name.ToLower().Trim() == property.Name.ToLower().Trim())
- {
- listName.Add(info.Name);
- }
- }
- }
- }
- // 转换要修改的字段为数组
- string[] ProNames = string.Join(",", listName).Split(",");
- // 寻找主键
- PropertyInfo pkProp = typeof(T).GetProperties().Where(p => p.GetCustomAttributes(typeof(KeyAttribute), false).Length> 0).FirstOrDefault();
- // 遍历修改, 并排除主键
- foreach (string Name in ProNames)
- {
- if (Name.ToLower() != pkProp.Name.ToLower())
- {
- _context.Entry(model).Property(Name).IsModified = true;
- }
- }
- //return db.SaveChanges();
- }
- /// <summary>
- /// 更新指定实体, 不更新指定字段, 如果每个表中有相同不更新的字段, 可以这样写, 扩展方法
- /// </summary>
- /// <typeparam name="T"> 数据表实体 Model 模型 </typeparam>
- /// <param name="modelNew"> 动态 JSON 数据 </param>
- /// <param name="fieldProNames"> 不更新的字段列表数组 </param>
- protected virtual void UpdateSpecify<T>(dynamic modelNew, string fieldProNames)
- {
- // 序列化动态 JSON 为字符串
- string JSON = modelNew.ToString();
- // 反序列化为数据表中的实体对象
- T model = JsonConvert.DeserializeObject<T>(JSON);
- // 把状态全部变为不可更改
- _context.Entry(model).State = EntityState.Unchanged;
- // 反序列化为动态对象中的属性
- var jsonModel = JsonConvert.DeserializeObject<dynamic>(JSON);
- // 定义一个 List 来添加属性
- List<string> listName = new List<string>();
- // 定义不需要更新的字段
- string fieldProNameses = "field1,field2,field3,CreateDate,Creator,IsDel,Updator,UpdateDate," + fieldProNames;
- // 动态添加要修改的字段
- foreach (PropertyInfo info in model.GetType().GetProperties())
- {
- // 如果 EF 表中有实体对象, 则排除, 否则更新会报错, 保留枚举
- if ((info.PropertyType.IsClass && info.PropertyType == typeof(String)) || info.PropertyType.IsClass == false)
- {
- // 解决大小写问题
- foreach (var property in jsonModel && !fieldProNameses.Split(",").Select(n => n.ToLower()).Contains(info.Name.ToLower()))
- {
- if (info.Name.ToLower().Trim() == property.Name.ToLower().Trim())
- {
- listName.Add(info.Name);
- }
- }
- }
- }
- // 转换要修改的字段为数组
- string[] ProNames = string.Join(",", listName).Split(",");
- // 寻找主键
- PropertyInfo pkProp = typeof(T).GetProperties().Where(p => p.GetCustomAttributes(typeof(KeyAttribute), false).Length> 0).FirstOrDefault();
- // 遍历修改, 并排除主键
- foreach (string Name in ProNames)
- {
- if (Name.ToLower() != pkProp.Name.ToLower())
- {
- _context.Entry(model).Property(Name).IsModified = true;
- }
- }
- //return db.SaveChanges();
- }
- }
- }
利用泛型 + 反射 添加动态修改数据表的方法完成之后去测试吧. 新建个 Controller
- using Microsoft.AspNetCore.Mvc;
- using System.Threading.Tasks;
- namespace ASP.NET.Core.EF
- {
- /// <summary > 测试修改控制器 </summary>
- [Route("api/[controller]")]
- [ApiController]
- public class DemoController : PageBase
- {
- /// <summary > 构造函数 </summary>
- /// <param name="context"> 依赖注入数据库连接 SQL</param>
- public DemoController(DBContext context) : base(context)
- {
- }
- /// <summary > 测试修改数据 </summary>
- /// <param name="modelModify"> 动态修改的 JSON 实体 </param>
- /// <returns></returns>
- [HttpPut]
- public async Task<IActionResult> PutModel(dynamic modelModify)
- {
- Update<MemberModel>(modelModify);
- Update<MemberModel>(modelModify, "testField1,testField2,testField3");
- UpdateSpecify<MemberModel>(modelModify);
- UpdateSpecify<MemberModel>(modelModify, "testField1,testField2,testField3");
- return Ok("修改成功, 请查询数据库");
- }
- }
- }
前端测试 JSON 数据
- {
- "id": 1,
- "testField1": "demo1",
- "testField2": "demo2",
- "testField3": "demo3",
- }
好了, 完功, 这样后端开发就很舒服了, 可以说轻松加愉快, 对于前端也很舒服! 解脱了很多时间!!
来源: https://www.cnblogs.com/wcp-boy/p/12488056.html