C#/.NET 中生成model类方法

后端api



public
class CodeGenApp {
     //上下文,用来区分是哪个数据库,下面GenerateModel()方法中,用哪个上下文就会查询哪个库的表
private readonly LandContext _ctx; private readonly CRMContext _crm; private readonly WMSContext _wms; public CodeGenApp(LandContext ctx, CRMContext crm, WMSContext wms) { _ctx = ctx; _crm = crm; _wms = wms; } public string GenerateModel(GenerateModelReq req) { if (req == null || string.IsNullOrWhiteSpace(req.TableName)) throw new Exception("请输入表名"); var table = req.TableName.Trim(); var ns = string.IsNullOrWhiteSpace(req.Namespace) ? "LandWebApi.Models" : req.Namespace.Trim(); var className = string.IsNullOrWhiteSpace(req.ClassName) ? ToPascal(table) : req.ClassName.Trim(); var tableAttr = string.IsNullOrWhiteSpace(req.TableAttributeName) ? table : req.TableAttributeName.Trim(); // 读取列信息(含列注释),限定当前数据库 var cols = _ctx.Database.SqlQuery<ColInfo>(@" SELECT c.ORDINAL_POSITION AS `Order`, c.COLUMN_NAME AS `Name`, c.DATA_TYPE AS `DataType`, c.CHARACTER_MAXIMUM_LENGTH AS `MaxLen`, c.IS_NULLABLE AS `IsNullable`, c.COLUMN_COMMENT AS `Comment` FROM INFORMATION_SCHEMA.COLUMNS c WHERE c.TABLE_NAME = @p0 AND c.TABLE_SCHEMA = DATABASE() ORDER BY c.ORDINAL_POSITION" , table).ToList(); if (cols == null || cols.Count == 0) throw new Exception($"未在数据库中找到表: {table}"); // 主键列(仅取第一个作为 [Key] 标注) var pk = _ctx.Database.SqlQuery<string>(@" SELECT k.COLUMN_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS t JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE k ON t.CONSTRAINT_NAME = k.CONSTRAINT_NAME AND t.TABLE_NAME = k.TABLE_NAME WHERE t.TABLE_NAME = @p0 AND t.TABLE_SCHEMA = DATABASE() AND t.CONSTRAINT_TYPE = 'PRIMARY KEY'", table).FirstOrDefault(); // 表注释 var tableComment = _ctx.Database.SqlQuery<string>(@" SELECT TABLE_COMMENT FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = @p0 AND TABLE_SCHEMA = DATABASE()", table).FirstOrDefault(); var sb = new StringBuilder(); sb.AppendLine("using System;"); sb.AppendLine("using System.ComponentModel.DataAnnotations;"); sb.AppendLine("using System.ComponentModel.DataAnnotations.Schema;"); sb.AppendLine(); sb.AppendLine($"namespace {ns}"); sb.AppendLine("{"); sb.AppendLine(" /// <summary>"); sb.AppendLine(!string.IsNullOrWhiteSpace(tableComment) ? $" /// {tableComment}" : $" /// Model for table: {table}"); sb.AppendLine(" /// </summary>"); sb.AppendLine($" [Table(\"{tableAttr}\")] "); sb.AppendLine($" public class {className}"); sb.AppendLine(" {"); foreach (var col in cols) { var isPk = !string.IsNullOrEmpty(pk) && string.Equals(pk, col.Name, StringComparison.OrdinalIgnoreCase); var csType = ToCSharpType(col.DataType, col.IsNullable); var propName = ToPropertyName(col.Name); var needColumnAttr = !string.Equals(propName, col.Name, StringComparison.Ordinal); // 保留原始列名映射 sb.AppendLine(" /// <summary>"); sb.AppendLine(!string.IsNullOrWhiteSpace(col.Comment) ? $" /// {col.Comment}" : $" /// {col.Name}"); sb.AppendLine(" /// </summary>"); if (isPk) sb.AppendLine(" [Key]"); if (needColumnAttr) sb.AppendLine($" [Column(\"{col.Name}\")]"); sb.AppendLine($" public {csType} {propName} {{ get; set; }}"); } sb.AppendLine(" }"); sb.AppendLine("}"); return sb.ToString(); } /// <summary> /// 将数据库列名转换为合法的 C# 属性名,保留语义,同时生成 Column 特性映射原始列名 /// </summary> private static string ToPropertyName(string name) { if (string.IsNullOrWhiteSpace(name)) return name; // 按下划线、空格、短横线拆分再拼接为 PascalCase var parts = name.Split(new[] { '_', ' ', '-' }, StringSplitOptions.RemoveEmptyEntries); var pascal = string.Join("", parts.Select(p => char.ToUpperInvariant(p[0]) + (p.Length > 1 ? p.Substring(1) : ""))); // 若开头不是字母或下划线,前置下划线使其成为合法标识符 if (!Regex.IsMatch(pascal, @"^[_a-zA-Z]")) { pascal = "_" + pascal; } return pascal; } /// <summary> /// 将表名/类名转换为 PascalCase(生成类名用) /// </summary> private static string ToPascal(string name) { if (string.IsNullOrWhiteSpace(name)) return name; var parts = name.Split(new[] { '_', ' ', '-' }, StringSplitOptions.RemoveEmptyEntries); var pascal = string.Join("", parts.Select(p => char.ToUpperInvariant(p[0]) + (p.Length > 1 ? p.Substring(1) : ""))); if (!Regex.IsMatch(pascal, @"^[_a-zA-Z]")) { pascal = "_" + pascal; } return pascal; } private static string ToCSharpType(string sqlType, string isNullable) { bool nullable = string.Equals(isNullable, "YES", StringComparison.OrdinalIgnoreCase); string t = sqlType?.ToLowerInvariant(); string core; switch (t) { case "int": core = "int"; break; case "bigint": core = "long"; break; case "smallint": core = "short"; break; case "tinyint": core = "byte"; break; case "bit": core = "int"; break; // 项目中多用 int? 表示 case "decimal": case "numeric": case "money": case "smallmoney": core = "decimal"; break; case "float": core = "double"; break; case "real": core = "float"; break; case "datetime": case "datetime2": case "smalldatetime": case "date": core = "DateTime"; break; case "time": core = "TimeSpan"; break; case "uniqueidentifier": core = "Guid"; break; default: return "string"; // varchar, nvarchar, text, etc. } return nullable ? core + "?" : core; } private class ColInfo { public long Order { get; set; } public string Name { get; set; } public string DataType { get; set; } public long? MaxLen { get; set; } public string IsNullable { get; set; } public string Comment { get; set; } } }


前端

 

<template>
  <div>
    <el-form :model="formData" label-width="100px" inline>
      <el-form-item label="表名" required>
        <el-input v-model="formData.TableName" placeholder="如 Land_SelfInventory_Main 或 shop_goods" style="width:260px" />
      </el-form-item>
      <el-form-item label="命名空间">
        <el-input v-model="formData.Namespace" placeholder="默认 LandWebApi.Models" style="width:260px" />
      </el-form-item>
      <el-form-item label="类名">
        <el-input v-model="formData.ClassName" placeholder="可选,不填自动推断" style="width:220px" />
      </el-form-item>
      <el-form-item label="Table特性">
        <el-input v-model="formData.TableAttributeName" placeholder="可选,默认等于表名" style="width:220px" />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" size="small" @click="handleGenerate">生成Model</el-button>
        <el-button size="small" @click="handleCopy" :disabled="!code">复制代码</el-button>
      </el-form-item>
    </el-form>

    <el-alert type="info" :closable="false" show-icon style="margin-bottom:8px" description="输入表名,调用后端接口读取数据库结构并生成C# Model源码。" />

    <el-input v-model="code" type="textarea" :rows="24" placeholder="生成的代码将显示在这里" />
  </div>
 </template>

<script>
import { genModel } from './api/api'

export default {
  name: 'codeGenModel',
  data () {
    return {
      formData: { TableName: '', Namespace: 'LandWebApi.Models', ClassName: '', TableAttributeName: '' },
      code: ''
    }
  },
  methods: {
    handleGenerate () {
      this.formData.TableName = (this.formData.TableName || '').trim()
      if (!this.formData.TableName) return this.$message.warning('请输入表名')
      const payload = { ...this.formData }
      console.log('[CodeGen] 请求参数:', payload)
      genModel(payload).then(res => {
        const data = (res && res.data) || res || {}
        const ok = (data.Code === 200) || (res && res.status === 200)
        if (!ok) return this.$message.error(data.Message || '生成失败')
        this.code = data.Data || ''
      })
    },
    handleCopy () {
      if (!this.code) return
      const ta = document.createElement('textarea')
      ta.value = this.code
      document.body.appendChild(ta)
      ta.select()
      document.execCommand('copy')
      document.body.removeChild(ta)
      this.$message.success('已复制到剪贴板')
    }
  }
}
</script>

<style scoped>
</style>

 

 

 

 

posted @ 2025-12-16 13:53  猪肉大葱  阅读(4)  评论(0)    收藏  举报