基于Entity Framework的Windows Forms多相似表操作:用字符串替代实体类
嘿,这个场景我之前做项目的时候也碰到过,为每个结构相似的表写重复的新增代码真的太折磨人了!咱们完全可以借助Entity Framework的动态特性加上反射来搞定,不用再写一堆重复逻辑。下面给你一步步拆解实现思路和代码:
核心思路
因为所有表的结构完全一致,对应的实体类属性也完全相同,所以我们可以通过选中的表名动态找到对应的实体类型,再用反射创建实体对象、给属性赋值,最后通过EF的动态DbSet完成数据插入,全程不用针对单个实体类写重复代码。
具体实现步骤
1. 把选中的表名传递到新增窗体
首先在主窗体MainForm里,监听ListBox的选中事件,把选中的表名传给AddForm:
private void listBoxTables_SelectedIndexChanged(object sender, EventArgs e) { if (listBoxTables.SelectedItem == null) return; var selectedTableName = listBoxTables.SelectedItem.ToString(); var addForm = new AddForm(selectedTableName); addForm.ShowDialog(); }
然后在AddForm的构造函数里接收并保存这个表名:
private readonly string _targetTableName; public AddForm(string targetTableName) { InitializeComponent(); _targetTableName = targetTableName; }
2. 动态获取实体类型并创建实例
在AddForm的保存按钮点击事件里,我们要做这几件事:
- 根据表名找到对应的实体类型(处理实体类和表名不一致的情况,比如用了
[Table]特性) - 反射创建实体对象
- 把文本框的值赋值给实体属性
- 动态获取
DbSet并插入数据
代码示例:
private void btnSave_Click(object sender, EventArgs e) { using (var context = new AppDbContext()) // 替换成你的EF上下文类名 { // 1. 根据表名获取对应的实体类型 Type entityType = GetEntityTypeByTableName(_targetTableName); if (entityType == null) { MessageBox.Show("找不到对应的实体类,请检查表名配置!"); return; } // 2. 创建实体对象实例 object entity = Activator.CreateInstance(entityType); // 3. 给实体属性赋值(假设文本框Name是"txt"+属性名,比如txtName对应Name属性) if (!AssignEntityProperties(entity, entityType)) { return; // 赋值出错直接返回 } // 4. 动态获取DbSet并添加数据 var dbSet = context.Set(entityType); dbSet.Add(entity); context.SaveChanges(); MessageBox.Show("数据新增成功!"); this.Close(); } } // 封装根据表名获取实体类型的方法 private Type GetEntityTypeByTableName(string tableName) { // 遍历当前程序集的所有类型,匹配表名 return Assembly.GetExecutingAssembly().GetTypes() .FirstOrDefault(t => // 先匹配实体类名(如果表名和类名一致) t.Name.Equals(tableName, StringComparison.OrdinalIgnoreCase) // 再匹配[Table]特性指定的表名 || t.GetCustomAttribute<TableAttribute>()?.Name.Equals(tableName, StringComparison.OrdinalIgnoreCase) == true); } // 封装给实体属性赋值的方法,加容错处理 private bool AssignEntityProperties(object entity, Type entityType) { foreach (var prop in entityType.GetProperties()) { // 找到对应的文本框控件 var txtBox = Controls.Find($"txt{prop.Name}", true).FirstOrDefault() as TextBox; if (txtBox == null || string.IsNullOrEmpty(txtBox.Text)) { continue; // 没有对应文本框或为空,跳过赋值 } // 安全转换文本框的值到属性类型 try { var value = Convert.ChangeType(txtBox.Text, prop.PropertyType); prop.SetValue(entity, value); } catch (FormatException) { MessageBox.Show($"「{prop.Name}」的输入格式不正确,请检查!"); return false; } catch (Exception ex) { MessageBox.Show($"赋值出错:{ex.Message}"); return false; } } return true; }
3. 优化:表名-实体类映射字典(可选)
如果你的表名和实体类名差异很大,用反射遍历的方式效率不高,可以提前维护一个映射字典,这样查找更快:
// 可以定义在AddForm或者全局工具类里 private static readonly Dictionary<string, Type> TableEntityMap = new Dictionary<string, Type> { {"T_Customers", typeof(Customer)}, {"T_Orders", typeof(Order)}, {"T_Products", typeof(Product)}, // 其他表和实体的映射关系 }; // 然后修改GetEntityTypeByTableName方法: private Type GetEntityTypeByTableName(string tableName) { TableEntityMap.TryGetValue(tableName, out var entityType); return entityType; }
注意事项
- 确保文本框的命名规则和实体属性一致(比如属性
UserName对应文本框txtUserName),如果不一致,你可以维护一个控件名-属性名的映射字典,或者在赋值时做对应处理。 - 如果实体包含非字符串类型的属性(比如
DateTime、int),一定要做好类型转换的容错处理,避免用户输入非法值导致程序崩溃。 - 这种动态方式虽然减少了重复代码,但调试时不如强类型直观,建议在关键步骤加调试日志或者断点。
内容的提问来源于stack exchange,提问作者LOG
相关产品推荐
相关产品推荐

