震惊!!!C# Code First 居然能自动生成数据库表?(文尾有完整源码)

C# CodeFirst

简介:
1.Code First 是一种使用 C# 编码来创建数据库的方法。它是 Entity Framework 中的一种数据访问方式,旨在通过编写 C# 代码来描述数据模型,然后自动生成数据库模式和表结构,从而简化了数据库开发的过程。

2.传统开发中,通常采用DbFirst的方式开发,先有数据库和表,再将对应的表转为实体。随着技术的发展,开始有了CodeFist的方式开发,先创建实体类,再通过实体类反向的创建数据库和表结构,微软的EF框架就是典型,本系统使用的ORM是SqlSugar,同样也支持CodeFisrt

注意:
1.CodeFirst可以快速开发,使用起来也要分阶段使用,比如早期随便搞,中后期需要禁用一些功能保证数据安全
2.数据库账号需要有比较高的权限,
3.Sqlite不支持删除列和修改列只能添加列

使用C# Code First 进行数据库开发时,我们需要进行以下几个步骤:

以控制台项目为例

环境信息:
在这里插入图片描述

0.前期准备

0.创建项目

1.创建目录

Program.cs外,均按照当前文件层级创建

1.编写实体类

Entity文件夹下, 实体类对于创建数据库表至关重要,所以要严格检查是否存在遗漏或其他容易出错的地方,
字段的特性时在创建库表时必不可少的条件,所以要创建符合业务的有效信息


#region AuthorInfo
// Author:sjx
// Date:2023-06-14 16:04
#endregion

namespace CodeFirstBySqlSugar.Entity;

/// <summary>
/// 用户表
/// </summary>
[SugarTable("User")]
[Description("用户表")]
public class User
{
    /// <summary>
    /// 用户ID,唯一标识用户的编号
    /// </summary>
    [SugarColumn(ColumnDescription = "用户ID,唯一标识用户的编号",IsPrimaryKey = true, IsIdentity = true)]
    public int UserId { get; set; }
    
    /// <summary>
    /// 用户名,用户的登录名或昵称
    /// </summary>
    [Required, MaxLength(20)]
    [SugarColumn(ColumnDescription = "用户名,用户的登录名或昵称")]
    public string UserName { get; set; }
    
    /// <summary>
    /// 密码,用户的登录密码
    /// </summary>
    [Required, MaxLength(50)]
    [SugarColumn(ColumnDescription = "密码,用户的登录密码")]
    public string PassWord { get; set; }
    
    /// <summary>
    /// 姓名,用户的真实姓名
    /// </summary>
    [SugarColumn(ColumnDescription = "姓名,用户的真实姓名")]
    public string FullName { get; set; }
    
    /// <summary>
    /// 性别,用户的性别,通常是男或女
    /// </summary>
    [SugarColumn(ColumnDescription = "性别,用户的性别,通常是男或女")]
    public string Gender { get; set; }
    
    /// <summary>
    /// 生日,用户的出生日期
    /// </summary>
    [SugarColumn(ColumnDescription = "生日,用户的出生日期")]
    public DateTime Birthday { get; set; }
    
    /// <summary>
    /// 手机号码,用户的手机号码
    /// </summary>
    [MaxLength(11)]
    [SugarColumn(ColumnDescription = "手机号码,用户的手机号码")]
    public string MobileNumber { get; set; }
    
    /// <summary>
    /// 电子邮件,用户的电子邮件地址
    /// </summary>
    [SugarColumn(ColumnDescription = "电子邮件,用户的电子邮件地址")]
    public string EmailAddress { get; set; }
    
    /// <summary>
    /// 地址,用户的联系地址
    /// </summary>
    [SugarColumn(ColumnDescription = "地址,用户的联系地址")]
    public string Address { get; set; }
    
    /// <summary>
    /// 注册时间,用户注册账号的时间
    /// </summary>
    [SugarColumn(ColumnDescription = "注册时间,用户注册账号的时间")]
    public DateTime RegistrationTime { get; set; } = DateTime.Now;
}

2.创建 DbContext 类

创建 SqlSugarHelper.cs类,该类只有一个数据库连接单例,用于帮助迁移数据库表和种子数据,可以根据项目的实际情况自行修改,或使用已存在的数据库连接

#region AuthorInfo
// Author:sjx
// Date:2023-06-14 16:23
#endregion

namespace CodeFirstBySqlSugar;

public class SqlSugarHelper
{
    /// <summary>
    /// 数据库单例
    /// </summary>
    /// <returns></returns>
    public static SqlSugarScope Db()
    {
        return new SqlSugarScope(new ConnectionConfig()
        {
            ConnectionString = "Data Source=localhost;Database=code_first_mysql;User ID=code_first_mysql;Password=code_first_mysql;pooling=true;port=3306;SslMode=none;CharSet=utf8;Convert Zero Datetime=True;Allow Zero Datetime=True;AllowLoadLocalInfile=true;",
            DbType = SqlSugar.DbType.MySql,
            InitKeyType = InitKeyType.Attribute,
            MoreSettings = new ConnMoreSettings()
            {
                IsAutoRemoveDataCache = true
            },
            IsAutoCloseConnection = true
        });
    }
}

3.配置实体类种子类

0.创建实现种子数据接口

SeedData文件夹下,创建 ISeedData.cs

#region AuthorInfo

// Author:sjx
// Date:2023-06-14 18:09

#endregion

namespace CodeFirstBySqlSugar.SeedData;

/// <summary>
/// 实体种子数据接口
/// </summary>
public class ISeedData { }
1.创建泛型接口

SeedData文件夹下,创建 ISqlSugarEntitySeedData.cs

#region AuthorInfo
// Author:sjx
// Date:2023-06-14 16:16
#endregion

namespace CodeFirstBySqlSugar.SeedData;

/// <summary>
/// 泛型接口 ISqlSugarEntitySeedData
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public interface ISqlSugarEntitySeedData<out TEntity> where TEntity: class, new()
{
    /// <summary>
    /// 种子数据
    /// </summary>
    /// <returns></returns>
    IEnumerable<TEntity> HasData();
}
2.创建种子数据

SeedData文件夹下,创建 UserSeedData.cs继承 创建泛型接口 ISeedDataISqlSugarEntitySeedData有几个实体类就创建多少个种子数据类

#region AuthorInfo
// Author:sjx
// Date:2023-06-14 16:15
#endregion

namespace CodeFirstBySqlSugar.SeedData;

/// <summary>
/// 种子数据
/// </summary>
public class UserSeedData:ISeedData,ISqlSugarEntitySeedData<User>
{
    /// <summary>
    /// 种子数据
    /// </summary>
    /// <returns></returns>
    public IEnumerable<User> HasData()
    {
        return new List<User>()
        {
            new()
            {
                UserId = 1,
                UserName = "admin",
                PassWord = "123456",
                FullName = "管理员",
                Gender = "男",
                Birthday = DateTime.Parse("1990-01-01"),
                MobileNumber = "13800138000",
                EmailAddress = "admin@admin.com",
                Address = "北京市海淀区",
            },
            new()
            {
                UserId = 2,                
                UserName = "user",
                PassWord = "123456",
                FullName = "用户",
                Gender = "男",
                Birthday = DateTime.Parse("1990-01-01"),
                MobileNumber = "13800138001",
                EmailAddress = "user@admin.com",
                Address = "北京市海淀区",
            }
        };
    }
}

4.迁移数据库

Program.cs创建迁移数据库方法 InitDatabase(),仔细阅读 注释代码解释

// 初始化表结构
void InitDatabase()
{
    // 简单写法
    // SqlSugarHelper.Db().CodeFirst.InitTables(typeof(User));
    
    // 高级写法
    
    // 获取所有实现了 SugarTableAttribute 特性的类
    var assembles = new[] { "CodeFirstBySqlSugar.dll" };
    
    // 获取所有实现了 SugarTableAttribute 特性的类
    var types = Assembly
        .LoadFrom(assembles[0])
        .GetTypes()
        .Where(m => m.GetCustomAttribute<SugarTable>() != null)
        .ToArray();

    // 遍历所有实现了 SugarTableAttribute 特性的类
    foreach (var type in types)
    {
        // 初始化表结构
        SqlSugarHelper.Db().CodeFirst.InitTables(type);
    }
    
    // 控制台输出数据库表迁移成功
    Console.WriteLine($"{DateTime.Now.ToString(CultureInfo.InvariantCulture)} 数据库表迁移成功");
}

代码解释:

这段代码是一个初始化数据库表结构的函数 InitDatabase(),用于在应用程序启动时自动迁移数据库表结构。函数使用了 SqlSugar 框架提供的 CodeFirst 功能,可以自动根据实体类生成数据库表结构。

函数分为两个部分:

第一部分使用了简单写法:

直接调用 SqlSugarHelper.Db().CodeFirst.InitTables(typeof(User)) 方法初始化 User 实体对应的数据库表结构。

第二部分使用了高级写法:

首先通过 Assembly.LoadFrom 方法加载程序集 CodeFirstBySqlSugar.dll,然后使用 GetTypes 方法获取程序集中所有的类型,再使用 LINQ 表达式筛选出所有实现了 SugarTableAttribute 特性的类型,存储在 types 数组中。

接着,函数遍历所有实现了 SugarTableAttribute 特性的类型,使用 SqlSugarHelper.Db().CodeFirst.InitTables(type) 方法初始化对应的数据库表结构。

最后,函数控制台输出数据库表迁移成功的提示信息。

5.实现种子数据

Program.cs创建迁移数据库方法 InsertData(),仔细阅读 注释代码解释

// 创建种子数据
void InsertData()
{
    // 简单写法
    // SqlSugarHelper.Db().Insertable(new UserSeedData().HasData().ToList()).ExecuteCommand();
    
    // 高级写法
    
    // 获取所有实现了 ISeedData 接口的类
    var assembles = new[] { "CodeFirstBySqlSugar.dll" };
    
    // 获取所有实现了 ISeedData 接口的类
    var baseType = typeof(ISeedData);
    
    // 获取所有实现了 ISeedData 接口的类
    var seedDataTypes = Assembly
        .LoadFrom(assembles[0])
        .GetTypes()
        .Where(m => baseType.IsAssignableFrom(m) && m is { IsClass: true, IsAbstract: false })
        .ToArray();

    // 遍历所有实现了 ISeedData 接口的类
    foreach (var seedDataType in seedDataTypes)
    {
        // 获取实现了 ISeedData 接口的类的实例
        var instance = Activator.CreateInstance(seedDataType) as ISeedData;
        
        // 获取实现了 ISeedData 接口的类的 HasData 方法
        var hasDataMethod = seedDataType.GetMethod("HasData");

        // 如果实现了 ISeedData 接口的类没有 HasData 方法,则跳过
        if (hasDataMethod?.Invoke(instance, null) is not IEnumerable seedData) continue;
        
        // 获取实现了 ISeedData 接口的类的泛型参数
        var data = seedData.Cast<object>();

        // 获取实现了 ISeedData 接口的类的泛型参数的泛型参数
        var entityType = seedDataType.GetInterfaces().First().GetGenericArguments().First();
        
        // 获取实现了 ISeedData 接口的类的泛型参数的泛型参数的表名
        var tableName = SqlSugarHelper.Db().EntityMaintenance.GetEntityInfo(entityType).DbTableName;

        // 将实现了 ISeedData 接口的类的泛型参数的泛型参数转换为 DataTable
        var seedDataTable = data.ToList().ToDataTable();
        
        // 设置 DataTable 的表名
        seedDataTable.TableName = tableName;
            
        // 将 DataTable 转换为 Storageable
        var storage = SqlSugarHelper.Db().Storageable(seedDataTable).ToStorage();
            
        // 执行插入操作
        var insertCount = storage.AsInsertable.ExecuteCommand();

        // 控制台输出插入数据的数量
        Console.WriteLine($"{DateTime.Now.ToString(CultureInfo.InvariantCulture)} {tableName} 表插入 {insertCount} 条数据");
    }
}

代码解释:

这段代码是一个创建种子数据的函数 InsertData(),用于在应用程序启动时自动向数据库中添加种子数据。函数使用了 SqlSugar 框架提供的 Insertable 和 Storageable 功能,可以将种子数据转换为 DataTable,并将 DataTable 转换为 Storageable,最后执行插入操作。

函数分为两个部分

第一部分使用了简单写法:

直接调用 SqlSugarHelper.Db().Insertable(new UserSeedData().HasData().ToList()).ExecuteCommand() 方法向数据库中插入 UserSeedData 类中定义的种子数据。

第二部分使用了高级写法:

首先通过 Assembly.LoadFrom 方法加载程序集 CodeFirstBySqlSugar.dll,然后使用 GetTypes 方法获取程序集中所有的类型,再使用 LINQ 表达式筛选出所有实现了 ISeedData 接口的类型,存储在 seedDataTypes 数组中。

接着,函数遍历所有实现了 ISeedData 接口的类型,使用 Activator.CreateInstance 方法创建类型的实例,并获取类型的 HasData 方法,如果类型没有实现 HasData 方法,则跳过。如果类型实现了 HasData 方法,则调用该方法获取种子数据,并使用 Cast<object> 方法将种子数据转换为 IEnumerable<object> 类型的序列。

然后,获取类型的泛型参数,即实体类的类型,并使用 SqlSugarHelper.Db().EntityMaintenance.GetEntityInfo(entityType).DbTableName 方法获取实体类对应的表名。接着,将种子数据转换为 DataTable,并设置 DataTable 的表名为实体类对应的表名。

最后,将 DataTable 转换为 Storageable,并执行插入操作。函数控制台输出插入数据的数量。

6.帮助类和全局引用类

0.帮助类 ObjectExtension.cs
#region AuthorInfo

// Author:sjx
// Date:2023-06-14 17:46

#endregion

namespace CodeFirstBySqlSugar;

/// <summary>
/// 对象扩展
/// </summary>
public static class ObjectExtension
{
    /// <summary>
    /// list转datatable
    /// </summary>
    /// <param name="list"></param>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public static DataTable ToDataTable<T>(this List<T> list)
    {
        DataTable result = new();
        if (list.Count <= 0) return result;
        var properties = list[0]?.GetType().GetProperties();
        if (properties == null) return result;
        foreach (var pi in properties)
        {
            var colType = pi.PropertyType;
            if (colType.IsGenericType && colType.GetGenericTypeDefinition() == typeof(Nullable<>))
            {
                colType = colType.GetGenericArguments()[0];
            }

            if (IsIgnoreColumn(pi))
                continue;
            result.Columns.Add(pi.Name, colType);
        }

        foreach (var t in list)
        {
            ArrayList tempList = new();
            foreach (var pi in properties)
            {
                if (IsIgnoreColumn(pi))
                    continue;
                var obj = pi.GetValue(t, null);
                tempList.Add(obj);
            }

            var array = tempList.ToArray();
            result.LoadDataRow(array, true);
        }
        return result;
    }

    /// <summary>
    /// 是否忽略列
    /// </summary>
    /// <param name="pi"></param>
    /// <returns></returns>
    private static bool IsIgnoreColumn(MemberInfo pi)
    {
        var sc = pi.GetCustomAttributes<SugarColumn>(false).FirstOrDefault(u => u.IsIgnore == true);
        return sc != null;
    }
}
1.全局引用类 GlobalUsings.cs(可以不使用全局引用类,可忽略)
#region AuthorInfo
// Author:sjx
// Date:2023-06-14 16:05
#endregion

global using System.Collections;
global using System.ComponentModel;
global using System.ComponentModel.DataAnnotations;
global using System.Globalization;
global using System.Reflection;
global using CodeFirstBySqlSugar;
global using CodeFirstBySqlSugar.Entity;
global using CodeFirstBySqlSugar.SeedData;
global using SqlSugar;
global using System.Data;

7.演示效果

  • 24
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值