本文同步发布在我的个人网站http://www.jiagoushi.cn
Poem需求描述
我们使用一个简单的项目来说明问题,这是一款针对全唐诗的小应用,可以对唐诗和作者进行简单的查询,可以对唐诗进行自定义的分类,还有一些简单的小游戏,比如可以将一句唐诗的顺序打乱,用户进行正确的排序等。数据库已经有了,在Sql Server中,将来可能需要支持Sqlite。数据模型如下:
增加领域层
我们要创建项目的领域层,在解决方案中增加一个空白的.Net Core的类库项目:
项目的名称是ZL.Poem.Core,这个项目作为领域层。
然后进入NuGet包管理器,增加ABP。首先我们创建模块,模块的名称为PoemCoreModule,结构与上面的PoemConsoleClientModule完全一样:
using Abp.Modules;
using System.Reflection;
namespace ZL.Poem.Core
{
public class PoemCoreModule : AbpModule
{
public override void Initialize()
{
IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
}
}
}
然后我们创建领域实体,首先创建一个文件夹,命名为Poems,在这个文件夹中保存与诗有关的实体。我们先从简单的做起,先创建一个实体类Poet(诗人):
using Abp.Domain.Entities;
namespace ZL.Poem.Core.Poems
{
/// <summary>
/// 诗人,从ABP Entity派生
/// </summary>
public class Poet : Entity
{
/// <summary>
/// 姓名
/// </summary>
public virtual string Name { get; set; }
/// <summary>
/// 介绍
/// </summary>
public virtual string Description { get; set; }
}
}
这时,项目的结构如下:
使用EF连接数据库
下面,我们数据访问层,使用EF从数据库中获取数据。在解决方案中增加另一个类库项目,命名为ZL.Poem.EF:
在NuGet中引入ABP、ABP.EntryFrameworkCore和Microsoft.EntityFrameworkCore.SqlServer。
创建DbContext
如果对DbContext不熟悉,可以先补充一下有关EF的知识。创建目录EntityFramework,创建类PoemGameDbContext,代码如下:
using Microsoft.EntityFrameworkCore;
using ZL.Poem.Core.Poems;
namespace ZL.Poem.EF.EntityFramework
{
public class PoemDbContext : AbpDbContext
{
public virtual DbSet<Poet> Poets { get; set; }
public PoemDbContext(DbContextOptions<PoemDbContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
//映射Poet到数据库表
modelBuilder.Entity<Poet>().ToTable("Poet");
//映射实体与数据库中的字段,将Id映射到数据库表的PoetID字段
modelBuilder.Entity<Poet>()
.Property(p => p.Id)
.HasColumnName("PoetID");
}
}
}
这里需要注意的是:Abp的实体都是从Entity中继承的,使用Id作为关键字标识,而我们已经存在的数据库表的关键字字段名是PoetID,这就需要在OnModelCreating中进行映射。
还有一个需要注意的问题是传入参数的类型,必须是DbContextOptions<PoemDbContext>
创建Abp模块
接下来,我们创建Abp模块,代码如下:
using Abp.EntityFrameworkCore;
using Abp.EntityFrameworkCore.Configuration;
using Abp.Modules;
using Abp.Reflection.Extensions;
using Microsoft.EntityFrameworkCore;
using ZL.Poem.Core;
using ZL.Poem.EF.EntityFramework;
namespace ZL.Poem.EF
{
[DependsOn(
typeof(PoemCoreModule),
typeof(AbpEntityFrameworkCoreModule))]
public class PoemDataModule : AbpModule
{
/* 在单元测试时,使用EF Core的内存数据库,不需要进行数据库注册 */
public bool SkipDbContextRegistration { get; set; }
/// <summary>
/// 初始化时注册DbContext
/// </summary>
public override void PreInitialize()
{
if (!SkipDbContextRegistration)
{
Configuration.Modules.AbpEfCore().AddDbContext<PoemDbContext>(options =>
{
var builder = options.DbContextOptions;
if (options.ExistingConnection != null)
{
builder.UseSqlServer(options.ExistingConnection);
}
else
{
builder.UseSqlServer(options.ConnectionString);
}
});
}
}
public override void Initialize()
{
IocManager.RegisterAssemblyByConvention(typeof(PoemDataModule).GetAssembly());
}
}
}
注意,这个模块中增加了依赖项的声明,由于这个模块依赖于PoemGameCoreModel和AbpEntityFrameworkCoreModule, 在DependsOn中进行了声明。
这里还有一个新的函数——PreInitialize,在模块初始化前需要执行的动作,这里需要做的工作是注册DbContext。
改造控制台客户端访问数据库
到目前为止,我们已经定义了一个领域实体,还定义了这个实体与数据库表的对应关系,现在可以改造我们的客户端程序,来访问数据库。
首先我们添加引用到Zl.Poem.ConsoleClient:
我们将ZL.Poem.Core和ZL.Poem.EF添加到依赖项中。
然后我们改造现有的模块,为这个模块增加依赖和数据库连接字串:
using Abp.Modules;
using System.Reflection;
using ZL.Poem.Core;
using ZL.Poem.EF;
namespace ZL.Poem.ConsoleClient
{
[DependsOn(
typeof(PoemDataModule),
typeof(PoemCoreModule))]
public class PoemConsoleClientModule : AbpModule
{
public override void PreInitialize()
{
Configuration.DefaultNameOrConnectionString = "Server=localhost; Database=PoemNew; Trusted_Connection=True;";
}
public override void Initialize()
{
IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
}
}
}
这里需要注意的是:在初始化前,需要设置数据库的连接字串。为了简化起见,这里直接进行了设置,以后我们会把连接串放到配置文件中去。
修改Service来访问数据库:
using Abp.Dependency;
using Abp.Domain.Repositories;
using System;
using ZL.Poem.Core.Poems;
namespace ZL.Poem.ConsoleClient
{
public class Service : ITransientDependency
{
public void Run()
{
//获取Poet的仓储
var repository = IocManager.Instance.Resolve<IRepository<Poet>>();
//获取id为1的诗人
var poet = repository.FirstOrDefault(1);
Console.WriteLine(poet.Name);
}
}
}
结果如下:
小结
- 解决方案中的每个项目都是一个ABP模块,在模块中定义模块之间的依赖关系。
- 可以通过使用AbpBootstrapper对入口模块进行初始化,由于存在依赖关系,相关模块也会被初始化。
- 可以使用IocManager获取需要使用的对象。
- 在模块的PreInitialize方法中设置初始化需要的参数。
下一步工作
下一步,我们需要增加其它几个实体到Poem.Core中,并增加应用层(Application)。