上一篇文章(https://www.cnblogs.com/meowv/p/12909558.html)完善了项目中的代码,接入了Swagger。本篇主要使用Entity Framework Core完成对数据库的访问,以及使用Code-First的方式进行数据迁移,自动创建表结构。

数据访问

.EntityFrameworkCore项目中添加我们的数据访问上下文对象MeowvBlogDbContext,继承自 AbpDbContext<T>。然后重写OnModelCreating方法。

OnModelCreating:定义EF Core 实体映射。先调用 base.OnModelCreating 让 abp 框架为我们实现基础映射,然后调用builder.Configure()扩展方法来配置应用程序的实体。当然也可以不用扩展,直接写在里面,这样一大坨显得不好看而已。

在abp框架中,可以使用 [ConnectionStringName] Attribute 为我们的DbContext配置连接字符串名称。先加上,然后再在appsettings.json中进行配置,因为之前集成了多个数据库,所以这里我们也配置多个连接字符串,与之对应。

本项目默认采用MySql,你可以选择任意你喜欢的。

//MeowvBlogDbContext.cs
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore; namespace Meowv.Blog.EntityFrameworkCore
{
[ConnectionStringName("MySql")]
public class MeowvBlogDbContext : AbpDbContext<MeowvBlogDbContext>
{
public MeowvBlogDbContext(DbContextOptions<MeowvBlogDbContext> options) : base(options)
{
} protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder); modelBuilder.Configure();
}
}
}
//appsettings.json
{
"ConnectionStrings": {
"Enable": "MySQL",
"MySql": "Server=localhost;User Id=root;Password=123456;Database=meowv_blog_tutorial",
"SqlServer": "",
"PostgreSql": "",
"Sqlite": ""
}
}

然后新建我们的扩展类MeowvBlogDbContextModelCreatingExtensions.cs和扩展方法Configure()。注意,扩展方法是静态的,需加static

//MeowvBlogDbContextModelCreatingExtensions.cs
using Microsoft.EntityFrameworkCore;
using Volo.Abp; namespace Meowv.Blog.EntityFrameworkCore
{
public static class MeowvBlogDbContextModelCreatingExtensions
{
public static void Configure(this ModelBuilder builder)
{
Check.NotNull(builder, nameof(builder));
...
}
}
}

完成上述操作后在我们的模块类MeowvBlogFrameworkCoreModule中将DbContext注册到依赖注入,根据你配置的值使用不同的数据库。在.Domain层创建配置文件访问类AppSettings.cs

//AppSettings.cs
using Microsoft.Extensions.Configuration;
using System.IO; namespace Meowv.Blog.Domain.Configurations
{
/// <summary>
/// appsettings.json配置文件数据读取类
/// </summary>
public class AppSettings
{
/// <summary>
/// 配置文件的根节点
/// </summary>
private static readonly IConfigurationRoot _config; /// <summary>
/// Constructor
/// </summary>
static AppSettings()
{
// 加载appsettings.json,并构建IConfigurationRoot
var builder = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", true, true);
_config = builder.Build();
} /// <summary>
/// EnableDb
/// </summary>
public static string EnableDb => _config["ConnectionStrings:Enable"]; /// <summary>
/// ConnectionStrings
/// </summary>
public static string ConnectionStrings => _config.GetConnectionString(EnableDb);
}
}

获取配置文件内容比较容易,代码中有注释也很容易理解。

值得一提的是,ABP会自动为DbContext中的实体创建默认仓储. 需要在注册的时使用options添加AddDefaultRepositories()

默认情况下为每个实体创建一个仓储,如果想要为其他实体也创建仓储,可以将 includeAllEntities 设置为 true,然后就可以在服务中注入和使用 IRepository<TEntity>IQueryableRepository<TEntity>

//MeowvBlogFrameworkCoreModule.cs
using Meowv.Blog.Domain;
using Meowv.Blog.Domain.Configurations;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.MySQL;
using Volo.Abp.EntityFrameworkCore.PostgreSql;
using Volo.Abp.EntityFrameworkCore.Sqlite;
using Volo.Abp.EntityFrameworkCore.SqlServer;
using Volo.Abp.Modularity; namespace Meowv.Blog.EntityFrameworkCore
{
[DependsOn(
typeof(MeowvBlogDomainModule),
typeof(AbpEntityFrameworkCoreModule),
typeof(AbpEntityFrameworkCoreMySQLModule),
typeof(AbpEntityFrameworkCoreSqlServerModule),
typeof(AbpEntityFrameworkCorePostgreSqlModule),
typeof(AbpEntityFrameworkCoreSqliteModule)
)]
public class MeowvBlogFrameworkCoreModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAbpDbContext<MeowvBlogDbContext>(options =>
{
options.AddDefaultRepositories(includeAllEntities: true);
}); Configure<AbpDbContextOptions>(options =>
{
switch (AppSettings.EnableDb)
{
case "MySQL":
options.UseMySQL();
break;
case "SqlServer":
options.UseSqlServer();
break;
case "PostgreSql":
options.UsePostgreSql();
break;
case "Sqlite":
options.UseSqlite();
break;
default:
options.UseMySQL();
break;
}
});
}
}
}

现在可以来初步设计博客所需表为:发表文章表(posts)、分类表(categories)、标签表(tags)、文章对应标签表(post_tags)、友情链接表(friendlinks)

.Domain层编写实体类,Post.cs、Category.cs、Tag.cs、PostTag.cs、FriendLink.cs。把主键设置为int型,直接继承Entity。关于这点可以参考ABP文档,https://docs.abp.io/zh-Hans/abp/latest/Entities

点击查看代码
//Post.cs
using System;
using Volo.Abp.Domain.Entities; namespace Meowv.Blog.Domain.Blog
{
/// <summary>
/// Post
/// </summary>
public class Post : Entity<int>
{
/// <summary>
/// 标题
/// </summary>
public string Title { get; set; } /// <summary>
/// 作者
/// </summary>
public string Author { get; set; } /// <summary>
/// 链接
/// </summary>
public string Url { get; set; } /// <summary>
/// HTML
/// </summary>
public string Html { get; set; } /// <summary>
/// Markdown
/// </summary>
public string Markdown { get; set; } /// <summary>
/// 分类Id
/// </summary>
public int CategoryId { get; set; } /// <summary>
/// 创建时间
/// </summary>
public DateTime CreationTime { get; set; }
}
}
//Category.cs
using Volo.Abp.Domain.Entities; namespace Meowv.Blog.Domain.Blog
{
/// <summary>
/// Category
/// </summary>
public class Category : Entity<int>
{
/// <summary>
/// 分类名称
/// </summary>
public string CategoryName { get; set; } /// <summary>
/// 展示名称
/// </summary>
public string DisplayName { get; set; }
}
}
//Tag.cs
using Volo.Abp.Domain.Entities; namespace Meowv.Blog.Domain.Blog
{
/// <summary>
/// Tag
/// </summary>
public class Tag : Entity<int>
{
/// <summary>
/// 标签名称
/// </summary>
public string TagName { get; set; } /// <summary>
/// 展示名称
/// </summary>
public string DisplayName { get; set; }
}
}
//PostTag.cs
using Volo.Abp.Domain.Entities; namespace Meowv.Blog.Domain.Blog
{
/// <summary>
/// PostTag
/// </summary>
public class PostTag : Entity<int>
{
/// <summary>
/// 文章Id
/// </summary>
public int PostId { get; set; } /// <summary>
/// 标签Id
/// </summary>
public int TagId { get; set; }
}
}
//FriendLink.cs
using Volo.Abp.Domain.Entities; namespace Meowv.Blog.Domain.Blog
{
/// <summary>
/// FriendLink
/// </summary>
public class FriendLink : Entity<int>
{
/// <summary>
/// 标题
/// </summary>
public string Title { get; set; } /// <summary>
/// 链接
/// </summary>
public string LinkUrl { get; set; }
}
}

创建好实体类后,在MeowvBlogDbContext添加DbSet属性

//MeowvBlogDbContext.cs
...
[ConnectionStringName("MySql")]
public class MeowvBlogDbContext : AbpDbContext<MeowvBlogDbContext>
{
public DbSet<Post> Posts { get; set; } public DbSet<Category> Categories { get; set; } public DbSet<Tag> Tags { get; set; } public DbSet<PostTag> PostTags { get; set; } public DbSet<FriendLink> FriendLinks { get; set; } ...
}
...

.Domain.Shared层添加全局常量类MeowvBlogConsts.cs和表名常量类MeowvBlogDbConsts.cs,搞一个表前缀的常量,我这里写的是meowv_,大家可以随意。代表我们的表名都将以meowv_开头。然后在MeowvBlogDbConsts中将表名称定义好。

//MeowvBlogConsts.cs
namespace Meowv.Blog.Domain.Shared
{
/// <summary>
/// 全局常量
/// </summary>
public class MeowvBlogConsts
{
/// <summary>
/// 数据库表前缀
/// </summary>
public const string DbTablePrefix = "meowv_";
}
}
//MeowvBlogDbConsts.cs
namespace Meowv.Blog.Domain.Shared
{
public class MeowvBlogDbConsts
{
public static class DbTableName
{
public const string Posts = "Posts"; public const string Categories = "Categories"; public const string Tags = "Tags"; public const string PostTags = "Post_Tags"; public const string Friendlinks = "Friendlinks";
}
}
}

Configure()方法中配置表模型,包括表名、字段类型和长度等信息。对于下面代码不是很明白的可以看看微软的自定义 Code First 约定:https://docs.microsoft.com/zh-cn/ef/ef6/modeling/code-first/conventions/custom

//MeowvBlogDbContextModelCreatingExtensions.cs
using Meowv.Blog.Domain.Blog;
using Meowv.Blog.Domain.Shared;
using Microsoft.EntityFrameworkCore;
using Volo.Abp;
using static Meowv.Blog.Domain.Shared.MeowvBlogDbConsts; namespace Meowv.Blog.EntityFrameworkCore
{
public static class MeowvBlogDbContextModelCreatingExtensions
{
public static void Configure(this ModelBuilder builder)
{
Check.NotNull(builder, nameof(builder)); builder.Entity<Post>(b =>
{
b.ToTable(MeowvBlogConsts.DbTablePrefix + DbTableName.Posts);
b.HasKey(x => x.Id);
b.Property(x => x.Title).HasMaxLength(200).IsRequired();
b.Property(x => x.Author).HasMaxLength(10);
b.Property(x => x.Url).HasMaxLength(100).IsRequired();
b.Property(x => x.Html).HasColumnType("longtext").IsRequired();
b.Property(x => x.Markdown).HasColumnType("longtext").IsRequired();
b.Property(x => x.CategoryId).HasColumnType("int");
b.Property(x => x.CreationTime).HasColumnType("datetime");
}); builder.Entity<Category>(b =>
{
b.ToTable(MeowvBlogConsts.DbTablePrefix + DbTableName.Categories);
b.HasKey(x => x.Id);
b.Property(x => x.CategoryName).HasMaxLength(50).IsRequired();
b.Property(x => x.DisplayName).HasMaxLength(50).IsRequired();
}); builder.Entity<Tag>(b =>
{
b.ToTable(MeowvBlogConsts.DbTablePrefix + DbTableName.Tags);
b.HasKey(x => x.Id);
b.Property(x => x.TagName).HasMaxLength(50).IsRequired();
b.Property(x => x.DisplayName).HasMaxLength(50).IsRequired();
}); builder.Entity<PostTag>(b =>
{
b.ToTable(MeowvBlogConsts.DbTablePrefix + DbTableName.PostTags);
b.HasKey(x => x.Id);
b.Property(x => x.PostId).HasColumnType("int").IsRequired();
b.Property(x => x.TagId).HasColumnType("int").IsRequired();
}); builder.Entity<FriendLink>(b =>
{
b.ToTable(MeowvBlogConsts.DbTablePrefix + DbTableName.Friendlinks);
b.HasKey(x => x.Id);
b.Property(x => x.Title).HasMaxLength(20).IsRequired();
b.Property(x => x.LinkUrl).HasMaxLength(100).IsRequired();
});
}
}
}

此时项目层级目录如下

代码优先

.EntityFrameworkCore.DbMigrations中新建模块类MeowvBlogEntityFrameworkCoreDbMigrationsModule.cs、数据迁移上下文访问对象MeowvBlogMigrationsDbContext.cs和一个Design Time Db Factory类MeowvBlogMigrationsDbContextFactory.cs

模块类依赖MeowvBlogFrameworkCoreModule模块和AbpModule。并在ConfigureServices方法中添加上下文的依赖注入。

//MeowvBlogEntityFrameworkCoreDbMigrationsModule.cs
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Modularity; namespace Meowv.Blog.EntityFrameworkCore.DbMigrations.EntityFrameworkCore
{
[DependsOn(
typeof(MeowvBlogFrameworkCoreModule)
)]
public class MeowvBlogEntityFrameworkCoreDbMigrationsModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAbpDbContext<MeowvBlogMigrationsDbContext>();
}
}
}

MeowvBlogMigrationsDbContextMeowvBlogDbContext没什么大的区别

//MeowvBlogMigrationsDbContext.cs
using Microsoft.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore; namespace Meowv.Blog.EntityFrameworkCore.DbMigrations.EntityFrameworkCore
{
public class MeowvBlogMigrationsDbContext : AbpDbContext<MeowvBlogMigrationsDbContext>
{
public MeowvBlogMigrationsDbContext(DbContextOptions<MeowvBlogMigrationsDbContext> options) : base(options)
{ } protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder); builder.Configure();
}
}
}

MeowvBlogMigrationsDbContextFactory类主要是用来使用Code-First命令的(Add-MigrationUpdate-Database ...)

需要注意的地方,我们在这里要单独设置配置文件的连接字符串,将.HttpApi.Hosting层的appsettings.json复制一份到.EntityFrameworkCore.DbMigrations,你用了什么数据库就配置什么数据库的连接字符串。

//appsettings.json
{
"ConnectionStrings": {
"Default": "Server=localhost;User Id=root;Password=123456;Database=meowv_blog"
}
}
//MeowvBlogMigrationsDbContextFactory.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Extensions.Configuration;
using System.IO; namespace Meowv.Blog.EntityFrameworkCore.DbMigrations.EntityFrameworkCore
{
public class MeowvBlogMigrationsDbContextFactory : IDesignTimeDbContextFactory<MeowvBlogMigrationsDbContext>
{
public MeowvBlogMigrationsDbContext CreateDbContext(string[] args)
{
var configuration = BuildConfiguration(); var builder = new DbContextOptionsBuilder<MeowvBlogMigrationsDbContext>()
.UseMySql(configuration.GetConnectionString("Default")); return new MeowvBlogMigrationsDbContext(builder.Options);
} private static IConfigurationRoot BuildConfiguration()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true); return builder.Build();
}
}
}

到这里差不多就结束了,默认数据库meowv_blog_tutorial是不存在的,先去创建一个空的数据库。

然后在Visual Studio中打开程序包管理控制台,将.EntityFrameworkCore.DbMigrations设为启动项目。

键入命令:Add-Migration Initial,会发现报错啦,错误信息如下:

Add-Migration : 无法将“Add-Migration”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确,然后再试一次。
所在位置 行:1 字符: 1
+ Add-Migration Initial
+ ~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Add-Migration:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException

这是因为我们少添加了一个包,要使用代码优先方式迁移数据,必须添加,Microsoft.EntityFrameworkCore.Tools

紧接着直接用命令安装Install-Package Microsoft.EntityFrameworkCore.Tools包,再试一遍

可以看到已经成功,并且生成了一个Migrations文件夹和对应的数据迁移文件

最后输入更新命令:Update-Database,然后打开数据瞅瞅。

完美,成功创建了数据库表,而且命名也是我们想要的,字段类型也是ok的。__efmigrationshistory表是用来记录迁移历史的,这个可以不用管。当我们后续如果想要修改添加表字段,新增表的时候,都可以使用这种方式来完成。

解决方案层级目录图,供参考

本篇使用Entity Framework Core完成数据访问和代码优先的方式创建数据库表,你学会了吗?

基于 abp vNext 和 .NET Core 开发博客项目 - 数据访问和代码优先的更多相关文章

  1. 基于 abp vNext 和 .NET Core 开发博客项目 - 博客接口实战篇(一)

    系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...

  2. 基于 abp vNext 和 .NET Core 开发博客项目 - 博客接口实战篇(二)

    系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...

  3. 基于 abp vNext 和 .NET Core 开发博客项目 - 博客接口实战篇(三)

    系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...

  4. 基于 abp vNext 和 .NET Core 开发博客项目 - 博客接口实战篇(四)

    系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...

  5. 基于 abp vNext 和 .NET Core 开发博客项目 - 博客接口实战篇(五)

    系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...

  6. 基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(一)

    系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...

  7. 基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(二)

    系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...

  8. 基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(三)

    系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...

  9. 基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(四)

    系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...

随机推荐

  1. 大数据并行计算框架Spark

    Spark2.1. http://dblab.xmu.edu.cn/blog/1689-2/ 0+入门:Spark的安装和使用(Python版) Spark2.1.0+入门:第一个Spark应用程序: ...

  2. tp5--路由的使用方法(深入)

    懒得写注释,直接上代码 配置文件Route: <?php use think\Route; //tp5路由测试 //动态注册 //Route::rule('路由表达式','路由地址','请求类型 ...

  3. 闲聊http1.1的6个方法

    GET :获取资源GET 方法用来请求访问已被 URI 识别的资源.指定的资源经服务器端解析后返回响应内容. POST :传输实体主体POST 方法用来传输实体的主体.虽然用 GET 方法也可以传输实 ...

  4. 2019-2020-1 20199325《Linux内核原理与分析》第四周作业

    start_kernel函数的执行过程 asmlinkage __visible void __init start_kernel(void) { char *command_line; char * ...

  5. Linux磁盘修复命令----fsck

    使用fsck命令修复磁盘时 一定要进入单用户模式去修复 语 法fsck.ext4[必要参数][选择参数][设备代号] 功 能fsck.ext4 命令: 针对ext4型文件系统进行检测 参数  -a 非 ...

  6. MySQL系列(四)

    本章内容: 主从复制 简介原理 Mysql主从同步脚本部署 读写分离 如果主宕机了,怎么办? 双主的情况 MySQL 备份及恢复方案 备份单个及多个数据库 mysqldump 的常用参数 如何增量恢复 ...

  7. KVM 一键批量创建虚拟机

    目录 一.原理 二.基础镜像 2.1.创建基础镜像 2.2. 完善基础镜像 2.3.基础镜像设置权限 3.4 设置 title 3.5.基础镜像XML 三.批量创建机器脚本 四.挂载磁盘多种方式 4. ...

  8. Redis(二):单机数据库的实现

    概要 本部分内容主要是研究单机数据库.分别介绍单机数据库的实现原理,数据库的持久化,Redis事件,服务器维护管理客户端以及单机服务器的运作机制. 数据库 数据库结构 Redis数据库由redis.h ...

  9. Java 线程池(ThreadPoolExecutor)原理分析与实际运用

    在我们的开发中"池"的概念并不罕见,有数据库连接池.线程池.对象池.常量池等等.下面我们主要针对线程池来一步一步揭开线程池的面纱. 有关java线程技术文章还可以推荐阅读:< ...

  10. 负载均衡服务之HAProxy https配置、四层负载均衡以及访问控制

    前文我们聊了下haproxy的访问控制ACL的配置,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/12817773.html:今天我们来聊一聊haproxy的h ...