Asp.Net Core 项目实战之权限管理系统(3) 通过EntityFramework Core使用PostgreSQL - Fonour - 博客园
Excerpt
0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之权限管理系统(2) 功能及实体设计 3 Asp.Net Core 项目实战之权限管理系统(3) 通过
0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有
1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端
2 Asp.Net Core 项目实战之权限管理系统(2) 功能及实体设计
3 Asp.Net Core 项目实战之权限管理系统(3) 通过EntityFramework Core使用PostgreSQL
4 Asp.Net Core 项目实战之权限管理系统(4) 依赖注入、仓储、服务的多项目分层实现
5 Asp.Net Core 项目实战之权限管理系统(5) 用户登录
6 Asp.Net Core 项目实战之权限管理系统(6) 功能管理
7 Asp.Net Core 项目实战之权限管理系统(7) 组织机构、角色、用户权限
8 Asp.Net Core 项目实战之权限管理系统(8) 功能菜单的动态加载
github源码地址
0 PostgreSQL安装及配置
0.0 PostgreSQL简介
既然Asp.Net Core最大的特性就是跨平台,就搭配使用一个可以跨平台的数据库。PostgreSQL是一个功能强大的开源数据库系统。经过长达15年以上的积极开发和不断改进,PostgreSQL已在可靠性、稳定性、数据一致性等获得了业内极高的声誉。目前PostgreSQL可以运行在所有主流操作系统上,包括Linux、Unix(AIX、BSD、HP-UX、SGI IRIX、Mac OS X、Solaris和Tru64)和Windows。PostgreSQL是完全的事务安全性数据库,完整地支持外键、联合、视图、触发器和存储过程(并支持多种语言开发存储过程)。它支持了大多数的SQL:2008标准的数据类型,包括整型、数值值、布尔型、字节型、字符型、日期型、时间间隔型和时间型,它也支持存储二进制的大对像,包括图片、声音和视频。PostgreSQL对很多高级开发语言有原生的编程接口,如C/C++、Java、.Net、Perl、Python、Ruby、Tcl 和ODBC以及其他语言等,也包含各种文档。
0.1 PostgreSQL安装及配置
自行去PostgreSQL官网下载符合你自己系统的版本,开始安装,从我自己的安装体验来看,没什么需要特别注意的地方,只需要按照提示一步步安装即可,在最后的时候根据需要选择以下语言,设置超级用户角色postgres的登录密码即可。
创建一个系统使用的角色
打开安装好的PostgreSQL数据库,输入密码进入管理界面。右键“登录角色”,创建一个名称为“fonour”的角色,在“角色权限”页签中把所有能能勾选的功能都勾选上。
右键PostgreSQL服务器,选择断开服务器。接着右键,单击“属性”菜单,在弹出窗口的用户名出输入刚才新建的“fonour”角色,输入密码并勾选记住密码。确定,连接即可。
1 使用EntityFrameworkCore的CodeFirst方式创建数据库
1.0 在Fonour.EntityFrameworkCore项目中创建DbContext
由于EF Core跟PostgreSQL都需要现学现用,EF Core的使用跟EF6.0还是有很多不同的,在使用的过程中遇到了不少的问题,尤其是针对PostgreSQL使用Guid类型的主键,后面会把这些坑做一个简单的记录。
0 添加相关依赖项
需要添加的相关依赖及说明如下:
- Npgsql.EntityFrameworkCore.PostgreSQL
PostgreSQL数据提供的支持EF Core的基础类库,是通过EF Core使用PostgreSQL数据库的根本。
- Npgsql.EntityFrameworkCore.PostgreSQL.Design
使用Guid(对应Postgre数据的类型为uuid)类型的主键必须,int/long类型的主键不添加也没问题。
- Microsoft.EntityFrameworkCore.Tools
EF Core工具,CodeFirst数据库迁移相关操作必须。
我们自己创建的一个类库项目,其中包含了组织机构、功能、角色、用户等实体的定义。
添加相关引用依赖的方式有多种,可以通过NuGet程序包管理器控制台的Install-Packege命令
1 2 3
| PM> Install-<span>Package Npgsql.EntityFrameworkCore.PostgreSQL PM</span>> Install-<span>Package Npgsql.EntityFrameworkCore.PostgreSQL.Design PM</span>> Install-Package Microsoft.EntityFrameworkCore.Tools
|
或者直接在NuGet程序包管理器中搜索相关类库,进行安装
最直接的方法是直接修改project.json配置文件,project.json配置文件最终修改后内容如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <span>{ </span>"version": "1.0.0-*"<span>,
</span>"dependencies"<span>: { </span>"Fonour.Domain": "1.0.0-0"<span>, </span>"Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final"<span>, </span>"NETStandard.Library": "1.6.0"<span>, </span>"Npgsql.EntityFrameworkCore.PostgreSQL": "1.0.1"<span>, </span>"Npgsql.EntityFrameworkCore.PostgreSQL.Design": "1.0.1"<span> },
</span>"frameworks"<span>: { </span>"netcoreapp1.0"<span>: { </span>"imports"<span>: [ </span>"dotnet5.6"<span>, </span>"portable-net45+win8"<span> ] } },
</span>"tools"<span>: { </span>"Microsoft.EntityFrameworkCore.Tools"<span>: { </span>"version": "1.0.0-preview2-final"<span>, </span>"imports"<span>: [ </span>"portable-net45+win8+dnxcore50"<span>, </span>"portable-net45+win8"<span> ] } } }</span>
|
注意,frameworks部分,如果默认是netstandard1.6框架,必须进行修改,否则会提示不支持相关依赖项。
1 创建DbContext
根据EF Core对多对多关联关系的要求,增加了UserRole、RoleMenu两个关联关系实体,同时对原有实体进行了调整。
1 2 3 4 5 6 7 8 9
| <span>public</span> <span>class</span><span> UserRole { </span><span>public</span> Guid UserId { <span>get</span>; <span>set</span><span>; } </span><span>public</span> User User { <span>get</span>; <span>set</span><span>; }
</span><span>public</span> Guid RoleId { <span>get</span>; <span>set</span><span>; } </span><span>public</span> Role Role { <span>get</span>; <span>set</span><span>; }
}</span>
|
1 2 3 4 5 6 7 8
| <span>public</span> <span>class</span><span> RoleMenu { </span><span>public</span> Guid RoleId { <span>get</span>; <span>set</span><span>; } </span><span>public</span> Role Role { <span>get</span>; <span>set</span><span>; }
</span><span>public</span> Guid MenuId { <span>get</span>; <span>set</span><span>; } </span><span>public</span> Menu Menu { <span>get</span>; <span>set</span><span>; } }</span>
|
在Fonour.EntityFrameworkCore项目下新建一个数据上下文操作类,命名为“FonourDBContext”,其中增加权限管理系统相关实体的DbSet的定义。最终代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| <span>public</span> <span>class</span><span> FonourDbContext : DbContext { </span><span>public</span> FonourDbContext(DbContextOptions<FonourDbContext> options) : <span>base</span><span>(options) {
} </span><span>public</span> DbSet<Department> Departments { <span>get</span>; <span>set</span><span>; } </span><span>public</span> DbSet<Menu> Menus { <span>get</span>; <span>set</span><span>; } </span><span>public</span> DbSet<Role> Roles { <span>get</span>; <span>set</span><span>; } </span><span>public</span> DbSet<User> Users { <span>get</span>; <span>set</span><span>; } </span><span>public</span> DbSet<UserRole> UserRoles { <span>get</span>; <span>set</span><span>; } </span><span>public</span> DbSet<RoleMenu> RoleMenus { <span>get</span>; <span>set</span><span>; }
</span><span>protected</span> <span>override</span> <span>void</span><span> OnModelCreating(ModelBuilder builder) { </span><span>//</span><span>UserRole关联配置</span> builder.Entity<UserRole><span>() .HasKey(ur </span>=> <span>new</span><span> { ur.UserId, ur.RoleId });
</span><span>//</span><span>RoleMenu关联配置</span> builder.Entity<RoleMenu><span>() .HasKey(rm </span>=> <span>new</span><span> { rm.RoleId, rm.MenuId }); builder.Entity</span><RoleMenu><span>() .HasOne(rm </span>=><span> rm.Role) .WithMany(r </span>=><span> r.RoleMenus) .HasForeignKey(rm </span>=> rm.RoleId).HasForeignKey(rm =><span> rm.MenuId);
</span><span>//</span><span>启用Guid主键类型扩展</span> builder.HasPostgresExtension(<span>"</span><span>uuid-ossp</span><span>"</span><span>);
</span><span>base</span><span>.OnModelCreating(builder); } }</span>
|
1.1 在Fonour.MVC项目中进行数据库连接相关配置
0 添加相关依赖项
在Asp.Net Core中,使用json格式的配置文件进行系统相关参数的配置,将相关配置文件通过ConfigurationBuilder进行统一管理,得到IConfigurationRoot的配置实例,获取相关配置文件配置节点的信息。想要使用配置文件相关服务,需要添加一下依赖。
- Microsoft.Extensions.Configuration
- Microsoft.Extensions.Configuration.FileExtensions
- Microsoft.Extensions.Configuration.Json
还需要添加对Fonour.EntityFrameworkCore项目的引用。
最终Fonour.MVC项目的project.json文件配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| <span>{ </span>"dependencies"<span>: { </span>"Microsoft.NETCore.App": "1.0.1"<span>, </span>"Microsoft.AspNetCore.Diagnostics": "1.0.0"<span>, </span>"Microsoft.AspNetCore.Server.IISIntegration": "1.0.0"<span>, </span>"Microsoft.AspNetCore.Server.Kestrel": "1.0.1"<span>, </span>"Microsoft.Extensions.Logging.Console": "1.0.0"<span>, </span>"Microsoft.AspNetCore.Mvc": "1.0.1"<span>, </span>"Microsoft.AspNetCore.StaticFiles": "1.0.0"<span>, </span>"Microsoft.Extensions.Configuration": "1.0.0"<span>, </span>"Microsoft.Extensions.Configuration.FileExtensions": "1.0.0"<span>, </span>"Microsoft.Extensions.Configuration.Json": "1.0.0"<span>, </span>"Fonour.EntityFrameworkCore": "1.0.0-*"<span> },
</span>"tools"<span>: { </span>"Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-preview2-final"<span> },
</span>"frameworks"<span>: { </span>"netcoreapp1.0"<span>: { </span>"imports"<span>: [ </span>"dotnet5.6"<span>, </span>"portable-net45+win8"<span> ] } },
</span>"buildOptions"<span>: { </span>"emitEntryPoint": <span>true</span><span>, </span>"preserveCompilationContext": <span>true</span><span> },
</span>"runtimeOptions"<span>: { </span>"configProperties"<span>: { </span>"System.GC.Server": <span>true</span><span> } },
</span>"publishOptions"<span>: { </span>"include"<span>: [ </span>"wwwroot"<span>, </span>"web.config"<span> ] },
</span>"scripts"<span>: { </span>"postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%"<span> ] }, </span>"runtimes"<span>: { </span>"win10-x64"<span>: {} } }</span>
|
1 增加appsettings.json配置文件
右键Fonour.MVC项目,新增一个Asp.Net配置文件类型,名称为appsettings.json的配置文件。
appsettings.json文件目前主要内容为定义数据库连接字符串,内容如下:
1 2 3 4 5
| <span>{ </span>"ConnectionStrings"<span>: { </span>"Default": "User ID=fonour;Password=123456;Host=localhost;Port=5432;Database=Fonour;Pooling=true;"<span> } }</span>
|
2 启用数据库连接
首先在Startup.cs中定义一个IConfigurationRoot的属性,然后在系统启动类Startup.cs的构造函数中,对配置文件进行管理。
1 2 3 4 5 6 7 8 9 10
| <span>public</span> IConfigurationRoot Configuration { <span>get</span><span>; } </span><span>public</span><span> Startup(IHostingEnvironment env) { </span><span>var</span> builder = <span>new</span><span> ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile(</span><span>"</span><span>appsettings.json</span><span>"</span>, optional: <span>true</span>, reloadOnChange: <span>true</span><span>) .AddJsonFile($</span><span>"</span><span>appsettings.{env.EnvironmentName}.json</span><span>"</span>, optional: <span>true</span><span>); builder.AddEnvironmentVariables(); Configuration </span>=<span> builder.Build(); }</span>
|
在ConfigureServices方法中获取数据库连接字符串,并添加数据库连接服务。
1 2 3 4 5 6 7 8 9 10 11 12
| <span>public</span> <span>void</span><span> ConfigureServices(IServiceCollection services) { </span><span>//</span><span>获取数据库连接字符串</span> <span>var</span> sqlConnectionString = Configuration.GetConnectionString(<span>"</span><span>Default</span><span>"</span><span>);
</span><span>//</span><span>添加数据上下文</span> services.AddDbContext<FonourDbContext>(options =><span> options.UseNpgsql(sqlConnectionString) );
services.AddMvc(); }</span>
|
1.2 使用CodeFirst数据库迁移命令创建数据库
在EntityFrameworkCore中数据库迁移有两种方式。
0 使用命令行工具
在应用程序根目录按住shift键同时单击鼠标右键,选择“在此处打开命令窗口”,输入数据库迁移的命令
1 2 3
| <span>dotnet ef migrations add Init
dotnet ef database update</span>
|
1 使用程序包管理器控制台
在程序包管理器控制台中默认项目选择Fonour.EntityFrameworkCore,输入以下命令,自动创建数据库迁移文件。
注意,一定要将Fonour.MVC设置为启动项目。
执行完成后,项目中增加了数据库迁移文件。
输入以下命令,进行数据库更新操作。
提示更新完成后,查看我们的数据库,会发现数据库及数据库表已经创建完成。
2 数据初始化
为保证系统正常运行,我们需要对系统进行数据初始化,要初始化的数据包括一下内容:
- 用户表插入一条用户名为admin的超级管理员用户信息
- 功能菜单表增加组织机构管理、角色管理、用户管理、功能管理四条基本数据。
- 组织机构表插入一条组织机构信息(主要是因为用户表存在一个DepartmentId的外键)
在Fonour.EntityFrameworkCore项目中增加一个新的数据初始化类,命名为SeedData.cs,修改内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| <span>public</span> <span>static</span> <span>class</span><span> SeedData { </span><span>public</span> <span>static</span> <span>void</span><span> Initialize(IServicePr { </span><span>using</span> (<span>var</span> context = <span>new</span><span> FonourDbCon { </span><span>if</span><span> (context.Users.Any()) { </span><span>return</span>; <span>//</span><span> 已经初始化过数据</span> <span> } Guid departmentId </span>=<span> Guid.NewGuid </span><span>//</span><span>增加一个部门</span> <span> context.Departments.Add( </span><span>new</span><span> Department { Id </span>=<span> departmentId, Name </span>= <span>"</span><span>Fonour集团总部</span><span>"</span><span>, ParentId </span>=<span> Guid.Empty } ); </span><span>//</span><span>增加一个超级管理员用户</span> <span> context.Users.Add( </span><span>new</span><span> User { UserName </span>= <span>"</span><span>admin</span><span>"</span><span>, Password </span>= <span>"</span><span>123456</span><span>"</span>, <span>// </span> Name = <span>"</span><span>超级管理员</span><span>"</span><span>, DepartmentId </span>=<span> departme } ); </span><span>//</span><span>增加四个基本功能菜单</span> <span> context.Menus.AddRange( </span><span>new</span><span> Menu { Name </span>= <span>"</span><span>组织机构管理</span><span>"</span><span>, Code </span>= <span>"</span><span>Department</span><span>"</span><span>, SerialNumber </span>= <span>0</span><span>, ParentId </span>=<span> Guid.Empty, Icon </span>= <span>"</span><span>fa fa-link</span><span>"</span><span> }, </span><span>new</span><span> Menu { Name </span>= <span>"</span><span>角色管理</span><span>"</span><span>, Code </span>= <span>"</span><span>Role</span><span>"</span><span>, SerialNumber </span>= <span>1</span><span>, ParentId </span>=<span> Guid.Empty, Icon </span>= <span>"</span><span>fa fa-link</span><span>"</span><span> }, </span><span>new</span><span> Menu { Name </span>= <span>"</span><span>用户管理</span><span>"</span><span>, Code </span>= <span>"</span><span>User</span><span>"</span><span>, SerialNumber </span>= <span>2</span><span>, ParentId </span>=<span> Guid.Empty, Icon </span>= <span>"</span><span>fa fa-link</span><span>"</span><span> }, </span><span>new</span><span> Menu { Name </span>= <span>"</span><span>功能管理</span><span>"</span><span>, Code </span>= <span>"</span><span>Department</span><span>"</span><span>, SerialNumber </span>= <span>3</span><span>, ParentId </span>=<span> Guid.Empty, Icon </span>= <span>"</span><span>fa fa-link</span><span>"</span><span> } ); context.SaveChanges(); } } }</span>
|
在Fonour.MVC项目的Startup.cs中Configure方法最后增加数据初始化的操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <span>public</span> <span>void</span><span> Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole();
</span><span>if</span><span> (env.IsDevelopment()) { </span><span>//</span><span>开发环境异常处理</span> <span> app.UseDeveloperExceptionPage(); } </span><span>else</span><span> { </span><span>//</span><span>生产环境异常处理</span> app.UseExceptionHandler(<span>"</span><span>/Shared/Error</span><span>"</span><span>); } </span><span>//</span><span>使用静态文件</span> <span> app.UseStaticFiles(); </span><span>//</span><span>使用Mvc,设置默认路由为系统登录</span> app.UseMvc(routes =><span> { routes.MapRoute( name: </span><span>"</span><span>default</span><span>"</span><span>, template: </span><span>"</span><span>{controller=Login}/{action=Index}/{id?}</span><span>"</span><span>); });
SeedData.Initialize(app.ApplicationServices); </span><span>//</span><span>初始化数据</span> }
|
运行程序。查看数据库,发现初始化数据已经生成成功。
3 踩过的一些坑
- 使用Guid类型的主键一定要添加”Npgsql.EntityFrameworkCore.PostgreSQL.Design”: “1.0.1”的引用,注意1.0.0版本是不支持的。
- 要想使用Guid类型的主键一定要在DbContext的 OnModelCreating 重写方法中启用uuid的扩展 builder.HasPostgresExtension(“uuid-ossp”);
- 关于多对关系的实体设计
在以前的EntityFramework中,多对多关系的实体,只需要创建两个实体,两个实体分别包含一个对方集合的导航属性即可,是不需要创建关联实体类的。如,User实体中包含一个ICollection Roles的导航属性,同时Role实体中包含一个ICollection Users的导航属性,EF会根据实体间的关系自动创建一个User_Role中间表,若想修改中间表名,进行相关配置即可。
EF Core中必须创建关联关系的实体才行。
4 总结
本节主要是研究怎样通过EntityFramework Core使用PostgreSQL,通过CodeFirst数据库迁移的方式根据设计好的实体进行数据库的创建,最后我们根据系统需要,进行了一些相关数据的初始化。
接下来要实现用户登录功能。