You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 03:41:56