专栏首页GreenLeaves.Net 从零开始构建一个框架之基本实体结构与基本仓储构建

.Net 从零开始构建一个框架之基本实体结构与基本仓储构建

本系列文章将介绍如何在.Net框架下,从零开始搭建一个完成CRUD的Framework,该Framework将具备以下功能,基本实体结构(基于DDD)、基本仓储结构、模块加载系统、工作单元、事件总线(EventBus,具有事件溯源的功能)、以及依赖注入管理系统.

1、简介

本文将通过源码和代码注释和文字说明来解释基本实体结构的构建和基本仓储的构建

2、实战

(1)、基本实体的构建

在OOP的概念之下,对象大致可以分为两类,持久化对象和非持久化对象.本文主要讨论的是持久化对象,即需要写入到数据库或者其他数据容器中的对象,也就是实体(当然这里不是所谓的实体,而是通过OOP技术构建出来的一个实体结构,这个结构需要满足日常开发中绝大多数的业务需求).接下去,就是使用OOP技术来构建这个实体结构.

首先这个实体既然需要写入数据库,那么它必定有一个主键Id.同时这个主键Id可以是任意数据类型,当然用的最多的就是GUID和INT作为主键.前面全局唯一,后者查询效率快.

所以,就有了如下结构:

    public interface IEntity<TPrimaryKey>
    {
        /// <summary>
        /// 实体的主键Id
        /// </summary>
        TPrimaryKey Id { get; set; }
    }

其次,以不同数据类型(GUID、int、string)为主键的实体类型,存在一些共有方法,比如需要编写更加语义化的ToString方法,所以当不同类型需要共同的实现的时候,这个时候就需要一层抽象,来处理这层关系,所以就有了如下结构:

  [Serializable]

当然这个结构中可以有任何的共有方法,只要它们能成功的抽象出来,就能写入到里面.

接着,基本实体就出现了,这里我分为两类,一类以int为主键,一类已Guid主键,为别写道两个类中,如下代码:

    /// <summary>
    /// 以int为主键的实体类型
    /// </summary>
    [Serializable]
    public abstract class Entity: Entity<int>
    {

    }

    /// <summary>
    /// 以Guid为为主键的实体类型
    /// </summary>
    [Serializable]
    public abstract class GEntity : Entity<Guid>
    {

    }

打上Serializable特性,方便序列化.这里不同的子类使用abstract来实现,也是为了提供各自实体的共有抽象属性(或者方法).到这一步,最最基本的实体抽象构建完毕,但是还没有结束,因为这个结构可以继续优化.使它可以为我们的业务更好的服务.所以需要持久化的实体必定存在一个创建的过程,可能该实体在某些业务下不需要修改、删除或者查询功能,但是它有极大的概率存在一个创建的过程,所以这里需要构建一个实体创建的抽象类,代码如下:

    public interface ICreationAudited
    {
        /// <summary>
        /// 创建该实体的用户Id
        /// </summary>
        int? CreatorUserId { get; set; }

        /// <summary>
        /// 创建当前实体时的时间
        /// </summary>
        DateTime CreationTime { get; set; }
    }

    [Serializable]
    public abstract class CreationAuditedEntity<TPrimaryKey> : Entity<TPrimaryKey>, ICreationAudited
    {
        public int? CreatorUserId { get; set; }

        public DateTime CreationTime { get; set; }

        /// <summary>
        /// 构造 当前实体注入内存时,给定创建时间,存在误差,因为从业务点击页面创建到实际生成该实体阶段存在时间差,但是这个时间差可以忽略不计
        /// </summary>
        protected CreationAuditedEntity()
        {
            CreationTime = DateTime.Now;
        }
    }

    /// <summary>
    /// 实体创建      主键为int
    /// </summary>
    [Serializable]     
    public abstract class CreationAuditedEntity : CreationAuditedEntity<int>
    {

    }

这里考虑文章大小,Guid的实体创建类型就不实现了,接下去只实现int.

最后实体创建的结构,构建完毕之后,在编写一个需要增删查改所有功能都具备的实体结构,整个实体结构大致就构建完毕了,代码如下:

    public interface IDeletionAudited
    {
        /// <summary>
        /// 删除实体的用户Id
        /// </summary>
        int? DeleterUserId { get; set; }

        /// <summary>
        /// 删除实体的时间
        /// </summary>
        DateTime? DeletionTime { get; set; }
    }

    public interface IModificationAudited
    {
        /// <summary>
        /// 最后一次修改实体的用户Id
        /// </summary>
        int? LastModifierUserId { get; set; }

        /// <summary>
        /// 最后一次修改实体的时间
        /// </summary>
        DateTime? LastModificationTime { get; set; }
    }

    public interface IFullAudited : IDeletionAudited, IModificationAudited, ICreationAudited
    {

    }

    [Serializable]
    public abstract class FullAuditedEntity<TPrimaryKey>:CreationAuditedEntity<TPrimaryKey>,IFullAudited
    {
        public int? DeleterUserId { get; set; }

        public DateTime? DeletionTime { get; set; }

        public int? LastModifierUserId { get; set; }

        public DateTime? LastModificationTime { get; set; }
    }

    /// <summary>
    /// 具有增删查该功能的实体结构 主键为int
    /// </summary>
    public abstract class FullAuditedEntity : FullAuditedEntity<int>
    {

    }

ok,到这里基本的实体结构构建完毕,当然这里你可以随意的扩展,比如构建各种各样的默认的实体类,如主键为string的只具有修改和删除的实体类.可以根据业务的实体特性来动态的扩展.也可以向所有的抽象实体类中添加任意的抽象属性或者方法.比如给Entity添加获取HashCode的共有方法.

(2)、基本仓储结构的构建

关于仓储就不多介绍了,可以自行上网查阅相关的文章,基本仓储结构是依赖于实体结构的。本文将基本Dapper构建一套基本仓储结构.

首先必须有一个仓储接口标识,这个标识本身不具有方法,单单只是一个标识,方便后期实现工作单元和模块加载系统时,辨别出程序集中的仓储类型.如下:

    /// <summary>
    /// 仓储接口
    /// </summary>
    public interface IRepository
    {

    }

接着,基于这个接口来构建Dapper通用仓储具有的基本的功能,即增删查改、分页、列表等功能.代码如下:

    /// <summary>
    /// Dapper通用仓储接口
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    /// <typeparam name="TPrimaryKey"></typeparam>
    public interface IDapperRepository<TEntity, TPrimaryKey> : IRepository where TEntity : class, IEntity<TPrimaryKey>
    {
        /// <summary>
        /// 添加一条实体信息
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        Task InsertAsync(TEntity entity);

        /// <summary>
        /// 删除一条实体信息
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        Task DeleteAsync(TEntity entity);

        /// <summary>
        /// 修改一条实体信息
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        Task UpdateAsync(TEntity entity);

        /// <summary>
        /// 根据主键Id异步获取一条数据信息
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        Task<TEntity> GetAsync(TPrimaryKey id);


    }

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 原型模式(创建型模式)

    整个抽象的游戏设施建造系统相对变化较慢,本例中只有一个Build的创建方法,而Build内部的方法实现,该实现依赖与各种具体的实现,而这些实现变化的非常频繁,现...

    郑小超.
  • FactoryMethod工厂方法模式(创建型模式)

    整个抽象的游戏设施建造系统相对变化较慢,本例中只有一个Build的创建方法,而Build内部的方法实现,该实现依赖与各种具体的实现,而这些实现变化的非常频繁,现...

    郑小超.
  • GenericFactoryMethod泛型工厂模式实现简单IOC功能

    泛型工厂理论上不算Gof23中设计模式之一,但是也算是一种非常好的设计模式,个人认为,废话不多说,先写个简单的抽象工厂,在写一个泛型工厂的例子来比较抽象和泛型的...

    郑小超.
  • 原型模式(创建型模式)

    整个抽象的游戏设施建造系统相对变化较慢,本例中只有一个Build的创建方法,而Build内部的方法实现,该实现依赖与各种具体的实现,而这些实现变化的非常频繁,现...

    郑小超.
  • 委托与事件-委托事件案例(三)

      这两天一直在想如何结合实际案例来结束委托与事件的讲解,下面讲解两个事例,用来加深对委托及事件的理解。

    小世界的野孩子
  • 开源导入导出库Magicodes.IE 导出教程

    通过如上代码片段我们实现 IExporterHeaderFilter 接口,IExporterHeaderFilter以便支持多语言、动态控制列展示等场景

    HueiFeng
  • 见到了“公司”定义一个Company类,那么见到了“字段”是不是也可定义一个Column类?

      既然见到了公司,我们可以定义一个Class Company ,那么我们见到了字段,是不是也可以定义一个Class ColumnInfo呢? 公司的描述信息类...

    用户1174620
  • CQRS+ES项目解析-Equinox

    今天我们来分析另一个开源的CQRS+ES项目:Equinox。该项目可以在github上下载并直接本地运行,项目地址:https://github.com/Ed...

    thz
  • 通俗易懂设计模式解析——访问者模式

      今天我们看的是访问者模式【Visitor Pattern】,我们理解访问者模式这个名称可能会有利于我们理解其核心代码块。我们看这么个例子:我去朋友家做客,那...

    小世界的野孩子
  • .NET基础拾遗(4)委托、事件、反射与特性

      委托这个概念对C++程序员来说并不陌生,因为它和C++中的函数指针非常类似,很多码农也喜欢称委托为安全的函数指针。无论这一说法是否正确,委托的的确确实现了和...

    Edison Zhou

扫码关注云+社区

领取腾讯云代金券