上一篇文章(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>();
}
}
}

MeowvBlogMigrationsDbContext和MeowvBlogDbContext没什么大的区别

//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-Migration 和 Update-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. 聊聊Spring Boot Actuator

    概述 在本文中,我们将介绍Spring Boot Actuator.我们将首先介绍基础知识,然后详细讨论Spring Boot 1.x和2.x中的可用内容. 我们将在Spring Boot 1.x中学 ...

  2. C#多线程(16):手把手教你撸一个工作流

    目录 前言 节点 Then Parallel Schedule Delay 试用一下 顺序节点 并行任务 编写工作流 接口构建器 工作流构建器 依赖注入 实现工作流解析 前言 前面学习了很多多线程和任 ...

  3. 使用spring boot创建fat jar APP

    文章目录 介绍 build和run fat jar和 fat war 更多配置 介绍 在很久很很久以前,我们部署web程序的方式是怎么样的呢?配置好服务器,将自己写的应用程序打包成war包,扔进服务器 ...

  4. Java反射机制概念及使用

    反射机制 —— 将类中的所有成员反射成对于的类. 以“com.test.Person”类为例                      转换对应的类                获取方法      ...

  5. history of program atan2(y,x)和pow(x,y)

    编年史 1951 – Regional Assembly Language 1952 – Autocode 1954 – IPL (LISP语言的祖先) 1955 – FLOW-MATIC (COBO ...

  6. 定了,这个vue.js开源项目,面试时,一定会考问

     因为现在的网店,都是用的商城系统, 而实体店都是入座后,扫码打开网上商城进行选购(餐饮,超市等),所以,vue.js迅速开发网上购物商城系统成为了香饽饽, 本人开源2020年4月开发的购物商城系统, ...

  7. nodeJS中express框架设置全局跨域请求头

    //设置跨域请求头 router.all('*', function(req, res, next) { res.header("Access-Control-Allow-Origin&qu ...

  8. MySQL - Show Global Status 整理

    2019独角兽企业重金招聘Python工程师标准>>> MySQL - Show Global Status 整理 原文来源:MySQL 5.5 Reference Manual 部 ...

  9. ISA Introduction

    介绍一下X86.MIPS.ARM三种指令集: 1. X86指令集 X86指令集是典型的CISC(Complex Instruction Set Computer)指令集. X86指令集外部看起来是CI ...

  10. ST函数(ST表)RMQ O(1)查询 离线

    ST算法是基于倍增的动态规划算法. #include<iostream> #include<cstdio> #include<cstdlib> #include&l ...