从零开始进行ABP项目开发(二)——领域层

本文同步发布在我的个人网站http://www.jiagoushi.cn

Poem需求描述

我们使用一个简单的项目来说明问题,这是一款针对全唐诗的小应用,可以对唐诗和作者进行简单的查询,可以对唐诗进行自定义的分类,还有一些简单的小游戏,比如可以将一句唐诗的顺序打乱,用户进行正确的排序等。数据库已经有了,在Sql Server中,将来可能需要支持Sqlite。数据模型如下:

image

增加领域层

我们要创建项目的领域层,在解决方案中增加一个空白的.Net Core的类库项目:


step-3-n.PNG

项目的名称是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; }

    }
}

这时,项目的结构如下:


step-4-n.PNG

使用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:


step-5-n.PNG

我们将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);
        }
    }
}

结果如下:


step-8.PNG

小结

  • 解决方案中的每个项目都是一个ABP模块,在模块中定义模块之间的依赖关系。
  • 可以通过使用AbpBootstrapper对入口模块进行初始化,由于存在依赖关系,相关模块也会被初始化。
  • 可以使用IocManager获取需要使用的对象。
  • 在模块的PreInitialize方法中设置初始化需要的参数。

下一步工作

下一步,我们需要增加其它几个实体到Poem.Core中,并增加应用层(Application)。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容