美文网首页.NET Core
ABP框架实战系列(一)-持久层介绍篇

ABP框架实战系列(一)-持久层介绍篇

作者: 于跃_198e | 来源:发表于2020-12-28 21:47 被阅读0次

    ABP框架实战系列(一)-持久层介绍篇

    数据持久化

    在开始持久层的介绍之前,我们先引入一个基础概念:持久化

    狭义的理解: “持久化”仅仅指把域对象永久保存到数据库中;广义的理解,“持久化”包括和数据库相关的各种操作(持久化就是将有用的数据以某种技术保存起来,将来可以再次取出来应用,数据库技术,将内存数据一文件的形式保存在永久介质中(磁盘等)都是持久化的意思。

    但是仅仅的持久化会使项目不可维护或者后期维护不利,简单的保存功能已经完全满足不了现在软件开发的模块性、可维护性、
    扩展性、分层性原则,所以就需要一种技术框架,将业务层和数据库之间保存的操作做到可维护性、扩展性、分层性,于是就出现“持久层”的概念

    数据持久层

    持久层:设计目标是为整个项目提供一个衔接高低层、统一、安全和并发的数据持久机制,完成对各种数据库进行持久化的编程工作,并为系统业务逻辑提供服务。数据持久层提供了数据访问方法,能够使程序员避免手动编写程序访问数据持久层,使其专注于业务逻辑的开发,并且能够在不同的项目中重用映射框架,大大简化了数据增删改查等功能的开发过程,同时又不丧失多层结构的天然优势,具备可伸缩性和可扩展性

    ORM

    ORM数据持久层的一种子实现,它通过将映射的机制,把数据库中的一条记录当做程序的一个类处理,这样在CURD的处理上,真正实现了面向对象开发,也将软件的后期维护周期大大缩短

    市面上 .Net Core ORM 工具多如牛毛,就列举如下四种常见框架,使用方式大同小异,此次链上一篇很不错的EF Core 的入门教程:

    EntityFrameWork Core(推荐关注该公众号,干货满满)

    [FreeSql]

    [Nhibernate-core]

    [MyBatis]

    ORM优缺点

    • 优点
      • ORM最大的优势,隐藏了数据访问细节,“封闭”的通用数据库交互,ORM的核心。他使得我们的通用数据库交互变得简单易行,并且完全不用考虑该死的SQL语句。快速开发,由此而来。
      • ORM使我们构造固化数据结构变得简单易行。在ORM年表的史前时代,我们需要将我们的对象模型转化为一条一条的SQL语句,通过直连或是DB helper在关系数据库构造我们的数据库体系。而现在,基本上所有的ORM框架都提供了通过对象模型构造关系数据库结构的功能。
    • 缺点
      • 对于复杂查询,ORM仍然力不从心。虽然可以实现,但是不值的。视图可以解决大部分calculated column,case ,group,having,order by, exists

    ABP 框架的 Entity FrameWork Core

    DbContext

    <font color=Violet>EF核心需要定义来自DbContext类。在ABP,我们应该从abpdbcontext获得,如下所示</font>

    public class MyDbContext : AbpDbContext
    {
        public DbSet<Product> Products { get; set; }
    
        public MyDbContext(DbContextOptions<MyDbContext> options)
            : base(options)
        {
        }
    }
    
    

    <font color=violet>构造函数必须得到 DbContextOptions<T> 如上面所示. 参数名称必须是选项。无法改变它,因为ABP将它作为匿名对象参数提供。</font>

    Configuration(配置数据库连接字符串)
    ABP Module Zero 中,将路径配置在appsettings.json文件中 配置格式基本如下

    "ConnectionStrings": {
        "Default": "Data Source = 10.28.253.2;Initial Catalog = EventCloudDb;User Id = sa;Password = Ecsgui123;"
    
      }
    

    而对配置文件的加载、以及DbContext的注册是通过如下代码完成的(ABP 默认生成)
    代码中,EFModule 继承自 ABPModule,表名EF 这个模块,是一个可插拔的模块,该模块下有三个初始化函数PreInitialize(预初始化)、Initialize(初始化)、PostInitialize(StartUp 完成之后执行)。

    其中,PreInitilize方法中,包含ABP 框架下对模块中的AbpEFCore模块的注册。

        [DependsOn(
            typeof(UniversalCoreModule), 
            typeof(AbpZeroCoreEntityFrameworkCoreModule))]
        public class UniversalEntityFrameworkModule : AbpModule
        {
            /* Used it tests to skip dbcontext registration, in order to use in-memory database of EF Core */
            public bool SkipDbContextRegistration { get; set; }
    
            public bool SkipDbSeed { get; set; }
    
            public override void PreInitialize()
            {
                if (!SkipDbContextRegistration)
                {
                    Configuration.Modules.AbpEfCore().AddDbContext<UniversalDbContext>(options =>
                    {
                        if (options.ExistingConnection != null)
                        {
                            UniversalDbContextConfigurer.Configure(options.DbContextOptions, options.ExistingConnection);
                        }
                        else
                        {
                            UniversalDbContextConfigurer.Configure(options.DbContextOptions, options.ConnectionString);
                        }
                    });
                }
            }
    
            public override void Initialize()
            {
                IocManager.RegisterAssemblyByConvention(typeof(UniversalEntityFrameworkModule).GetAssembly());
            }
    
            public override void PostInitialize()
            {
                if (!SkipDbSeed)
                {
                    SeedHelper.SeedHostDb(IocManager);
                }
            }
        }
    
    

    在熟悉了上述DbContext加载配置的过程后,我们就相对应的会来到创建DbContext部分,这部分内容,ABP 模板在UniversalDbContextFactory中,实现了对象DbContext的创建

    1、创建DbContextOptionBuilder创建

    2、加载配置文件信息到内存

    3、初始化Builder 信息

    4、new 一个新的 DbContext对象

    
     public class Git_ECSGUI_UniversalDbContextFactory : IDesignTimeDbContextFactory<Git_ECSGUI_UniversalDbContext>
        {
            public Git_ECSGUI_UniversalDbContext CreateDbContext(string[] args)
            {
                var builder = new DbContextOptionsBuilder<Git_ECSGUI_UniversalDbContext>();
                var configuration = AppConfigurations.Get(WebContentDirectoryFinder.CalculateContentRootFolder());
    
                Git_ECSGUI_UniversalDbContextConfigurer.Configure(builder, configuration.GetConnectionString(Git_ECSGUI_UniversalConsts.ConnectionStringName));
    
                return new Git_ECSGUI_UniversalDbContext(builder.Options);
            }
        }
    
    

    ABP 的仓储

    讲完EF Core中的DbContext在ABP中的创建过程,我们将视线转到Repositories(仓库)中过来,他是领域层和持久之间的桥梁,存储库用于从高层抽象数据访问。
    在ABP 框架中领域和持久化层之间的媒介,使用一种类似集合的接口来访问实体,每个实体(或者聚合根)使用给一个分离的仓库

    默认仓储

    在ABP里,一个仓储类实现IRepository<TEntity,TPrimaryKey>接口。ABP默认地为每个实体类型自动创建一个默认仓储。你可以直接注入IRepository<TEntity>(或IRepository<TEntity,TPrimaryKey>)。一个应用服务使用仓储把一个实体插入数据库的例子:PersonService构造器注入IRepository<Person>并使用Insert方法。

    
    public class PersonService : IPersonService
    {
        private readonly IRepository<Person> _personRepository;
    
        public PersonService(IRepository<Person> personRepository)
        {
            _personRepository = personRepository;
        }
    
        public void CreatePerson(CreatePersonInput input)
        {        
            person = new Person { Name = input.Name, EmailAddress = input.EmailAddress };
            _personRepository.Insert(person);
        }
    }
    
    

    自定义仓储

    只有当实体需要创建一个自定义的仓储方法时,才需要你创建一个仓储类。

    
        public abstract class Git_ECSGUI_UniversalRepositoryBase<TEntity, TPrimaryKey> : EfCoreRepositoryBase<Git_ECSGUI_UniversalDbContext, TEntity, TPrimaryKey>
            where TEntity : class, IEntity<TPrimaryKey>
        {
            protected Git_ECSGUI_UniversalRepositoryBase(IDbContextProvider<Git_ECSGUI_UniversalDbContext> dbContextProvider)
                : base(dbContextProvider)
            {
            }
    
            // Add your common methods for all repositories
        }
    
        /// <summary>
        /// Base class for custom repositories of the application.
        /// This is a shortcut of <see cref="Git_ECSGUI_UniversalRepositoryBase{TEntity,TPrimaryKey}"/> for <see cref="int"/> primary key.
        /// </summary>
        /// <typeparam name="TEntity">Entity type</typeparam>
        public abstract class Git_ECSGUI_UniversalRepositoryBase<TEntity> : Git_ECSGUI_UniversalRepositoryBase<TEntity, int>, IRepository<TEntity>
            where TEntity : class, IEntity<int>
        {
            protected Git_ECSGUI_UniversalRepositoryBase(IDbContextProvider<Git_ECSGUI_UniversalDbContext> dbContextProvider)
                : base(dbContextProvider)
            {
            }
    
            // Do not add any method here, add to the class above (since this inherits it)!!!
        }
    
    

    要实现自定义存储库,只需从上面创建的应用程序特定的基础存储库类中获得。

    
            public interface IUniversalTaskRepository : IRepository<Task>
        {
            List<Task> GetTaskByAssignedPersonId(long taskId);
        }
    
        public UniversalTaskRepository:Git_ECSGUI_UniversalRepositoryBase, IUniversalTaskRepository
        {
            public UniversalTaskRepository(IDbContextProvider<Git_ECSGUI_UniversalDbContext> dbContextProvider) : base(dbContextProvider)
            {
            }
    
            /// <summary>
            /// 获取某个用户分配了哪些任务
            /// </summary>
            /// <param name="personId">用户Id</param>
            /// <returns>任务列表</returns>
            public List<Task> GetUniversalTaskId(long taskId)
            {
                var query = GetAll();
    
                if (taskId > 0)
                {
                    query = query.Where(t => t.taskid == taskId);
                }
    
                return query.ToList();
            }
        }
    
    

    仓储的注意事项

    • 仓储方法中,ABP自动进行数据库连接的开启和关闭。
    • 仓储方法被调用时,数据库连接自动开启且启动事务。
    • 当仓储方法调用另外一个仓储的方法,它们实际上共享的是同一个数据库连接和事务。
    • 仓储对象都是暂时性的,因为IRepository接口默认继承自ITransientDependency接口。所以,仓储对象只有在需要注入的时候,才会由Ioc容器自动创建新实例。
    • 默认的泛型仓储能满足我们大部分的需求。只有在不满足的情况下,才创建定制化的仓储。

    博主GitHub地址

    https://github.com/yuyue5945

    关注公众号留下您的困惑或见解

    相关文章

      网友评论

        本文标题:ABP框架实战系列(一)-持久层介绍篇

        本文链接:https://www.haomeiwen.com/subject/eyfxoktx.html