Data layer to a wave?

Preface: since it was created pigeon finished project for so long, finally remembered my account & password ...
return to the topic, I have come to try to be the data layer.
For a blog system may need to check the article additions and deletions to change the operation at least, in addition to the operation of the article, the author also need access to relevant information and circumstances of the system. Of course, a personal blog system there may be a user (temporarily not considered to achieve).
Above should be enough to constitute the basic components of a blog system.
Then their relationship is probably this:
Author: article = 1: n (one author can be a number of articles)
article: System access = 1: 1 (an article about a record, record of the article available)

ok, kinda simple relationship (hey, mainly to do simple)
then this would Framework Core and use the Entity PostgreSQL to help me complete the data persistence layer operation.
Nuget need to install some packages:

  • Npgsql.EntityFrameworkCore.PostgreSQL
    PostgreSQL data provided support EF Core foundation class library, simply using PostgreSQL database by EF Core.
  • Npgsql.EntityFrameworkCore.PostgreSQL.Design
    using the Guid (corresponding to the data type Postgre UUID) type primary key must, int / long type of the primary key is not added, no problem.
    Next, create an entity.
    A substantial portion of the article:
     /// <summary>
    /// 文章实体
    /// </summary>
    public class Article
    {
        /// <summary>
        /// 文章Id
        /// </summary>
        public int ArticleId { get; set; }
        /// <summary>
        /// 文章标题
        /// </summary>
        public string Title { get; set; } = default!;
        /// <summary>
        /// 文章内容(客户端传输base64编码内容,服务端将内容转为文本后存为静态文件,再将文件路径存入数据库)
        /// </summary>
        public string Content { get; set; } = default!;
        /// <summary>
        /// 文章创建时间
        /// </summary>
        public DateTime CreateTime { get; set; } = DateTime.Now;
        /// <summary>
        /// 文章修改时间(若未被修改则该时间为空)
        /// </summary>
        public DateTime? ModifyTime { get; set; }

        //=================文章与作者的关系=======================
        public virtual int AuthorId { get; set; }
        public virtual Author Author { get; set; } = default!;
        //=================文章与文章记录关系=====================
        public virtual int LogId { get; set; }
        public virtual ArticleLog Log { get; set; }
    }

Record substantial portion of the article:

    /// <summary>
    /// 文章记录
    /// </summary>
    public class ArticleLog
    {
        /// <summary>
        /// 文章记录Id
        /// </summary>
        public int ArticleLogId { get; set; }
        /// <summary>
        /// 文章浏览次数
        /// </summary>
        public int ViewNum { get; set; }
        //===============文章与记录的关系==================
        //因为文章与记录为一对一关系,所以只需一方有个外键就行了。在这里我将外键映射放在文章实体中
        //public virtual int ArticleId { get; set; }
        public virtual Article Article { get; set; } = default!;
    }

Part of the entity:

    /// <summary>
    /// 作者实体
    /// </summary>
    public class Author
    {
        /// <summary>
        /// 作者Id
        /// </summary>
        public int AuthorId { get; set; }
        /// <summary>
        /// 名称
        /// </summary>
        public string Name { get; set; } = default!;
        /// <summary>
        /// 作者账号
        /// </summary>
        public string Account { get; set; } = default!;
        /// <summary>
        /// 作者账号的密码
        /// </summary>
        public string Password { get; set; } = default!;
        /// <summary>
        /// 上次登录时间
        /// </summary>
        public DateTime LoginTime { get; set; }
        //=================作者与文章的关系================
        public virtual IEnumerable<Article>? Articles { get; set; }
    }

Note:
1. If the open air may be used in the type of reference type is not initialized, a warning or error type is empty (not through the compiler cause), the above referenced type of code used for default!initializing (not seem problem), but, to do so just by the compiler, and its assignment the result was null.
2. The relationship between the entity need to use the virtual keyword modifications

Next add the mapping relationship (IEntityTypeConfiguration use EFCore defined for us Interface):
mapping the relationship between the entity and articles database:

    public class ArticleConfig : IEntityTypeConfiguration<Article>
    {
        public void Configure(EntityTypeBuilder<Article> builder)
        {
            //在数据库中该实体被映射为表:article
            builder.ToTable("article");
            //以实体中ArticleId字段映射为数据库中名称为pk_id的主键
            builder.HasKey(k => k.ArticleId);
            //将主键映射为pk_id为名称、INTEGER为类型的自增长字段
            builder.Property(k => k.ArticleId).ValueGeneratedOnAdd().HasColumnType("INTEGER").HasColumnName("pk_id");
            //实体中Content字段段映射为数据库中名称为content、类型为VARCHAR(50)非空的字段
            builder.Property(p => p.Title).HasColumnType("VARCHAR(50)").HasColumnName("title").IsRequired();
            //实体中Content字段段映射为数据库中名称为content、类型为VARCHAR(512)非空的字段
            builder.Property(p => p.Content).HasColumnType("VARCHAR(512)").HasColumnName("content").IsRequired();
            //实体中CreateTime字段段映射为数据库中名称为create_time、类型为DATE的非空字段
            builder.Property(p => p.CreateTime).HasColumnType("DATE").HasColumnName("create_time").IsRequired();
            //实体中ModifyTime字段段映射为数据库中名称为modify_time、类型为DATE的可空字段
            builder.Property(p => p.ModifyTime).HasColumnType("DATE").HasColumnName("modify_time").IsRequired(false);
            //文章与作者之间的关系,使用文章实体中AuthorId字段作为外键,映射为数据库中名为fk_article_id、类型为INTEGER的非空外键
            builder.Property(f => f.AuthorId).HasColumnType("INTEGER").HasColumnName("fk_article_id").IsRequired();
            builder.HasOne(o => o.Author).WithMany(m => m.Articles).HasForeignKey(f => f.AuthorId);
            //文章与文章记录之间的关系,使用文章实体中LogId字段作为外键,映射为数据库中名为fk_log_id、类型为INTEGER的非空外键
            builder.Property(f=>f.LogId).HasColumnType("INTEGER").HasColumnName("fk_log_id").IsRequired();
            builder.HasOne(o => o.Log).WithOne(o => o.Article).HasForeignKey<Article>(f => f.LogId);
            //文章与作者关系已在作者实体配置,此处不需要重复配置
        }
    }

Article records mapping relationships between entities and database:

  /// <summary>
    /// 文章记录实体与数据库之间映射关系
    /// </summary>
    public class ArticleLogConfig : IEntityTypeConfiguration<ArticleLog>
    {
        public void Configure(EntityTypeBuilder<ArticleLog> builder)
        {
            //在数据库中该实体被映射为表:article_log
            builder.ToTable("article_log");
            //以实体中ArticleLogId字段映射为数据库中名称为pk_id、INTEGER类型的自增长主键
            builder.HasKey(k => k.ArticleLogId);
            //将主键映射为pk_id为名称、INTEGER为类型的自增长字段
            builder.Property(k => k.ArticleLogId).ValueGeneratedOnAdd().HasColumnType("INTEGER").HasColumnName("pk_id");
            //实体中ViewNum字段段映射为数据库中名称为view_num、类型为INTEGER的非空字段
            builder.Property(p => p.ViewNum).HasColumnType("INTEGER").HasColumnName("view_num").IsRequired();
            //映射关系已经在文章配置中配置,所以此处不需要配置
        }
    }

The mapping between the author and the entity database

     /// <summary>
    /// 作者实体与数据库之间映射关系
    /// </summary>
    public class AuthorConfig : IEntityTypeConfiguration<Author>
    {
        public void Configure(EntityTypeBuilder<Author> builder)
        {
            //在数据库中该实体被映射为表:author
            builder.ToTable("author");
            //以实体中AuthorId字段映射为数据库中主键
            builder.HasKey(k => k.AuthorId);
            //将主键映射为pk_id为名称、INTEGER为类型的自增长字段
            builder.Property(k => k.AuthorId).ValueGeneratedOnAdd().HasColumnType("INTEGER").HasColumnName("pk_id");
            //实体中Name字段段映射为数据库中名称为name、类型为VARCHAR(20)的非空字段
            builder.Property(p => p.Name).HasColumnType("VARCHAR(20)").HasColumnName("name").IsRequired();
            //实体中Account字段段映射为数据库中名称为account、类型为VARCHAR(40)的非空字段
            builder.Property(p => p.Account).HasColumnType("VARCHAR(40)").HasColumnName("account").IsRequired();
            //实体中Password字段段映射为数据库中名称为account、类型为VARCHAR(32)的非空字段
            //密码部分使用MD5摘要后存入数据库(毕竟不能限制密码长度=_=)
            builder.Property(p => p.Password).HasColumnType("VARCHAR(32)").HasColumnName("password").IsRequired();
            //实体中LoginTime字段段映射为数据库中名称为login_time、类型为DATE的非空字段
            builder.Property(p => p.LoginTime).HasColumnType("DATE").HasColumnName("login_time").IsRequired();
        }
    }

Well, with the data entity mapping rules, but also help us to complete the mapping of context, emmmmmmmmm called DbManager it.

    /// <summary>
    /// 管理数据库的连接与实体的映射
    /// </summary>
    public class DbManager:DbContext
    {
        //数据库连接字符串
        private string? _connectString;
        public DbManager(string connectString)
        {
            _connectString = connectString;
        }
        /// <summary>
        /// 应用实体映射规则(没错就是我们创建的以Config结尾的那些类)
        /// </summary>
        /// <param name="modelBuilder"></param>
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            //这里将自动加载所有的映射类=_=
            //如果希望手动加载,则(以AuthorConfig为例):
            //modelBuilder.ApplyConfiguration(new AuthorConfig());
            //使用Assembly.GetExecutingAssembly();会有性能问题
            //使用typeof(TSelf).Assembly表示当前程序集
            modelBuilder.ApplyConfigurationsFromAssembly(typeof(DbManager).Assembly);
            base.OnModelCreating(modelBuilder);
        }
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (_connectString!=null)
            {
                optionsBuilder.UseNpgsql(_connectString);
            }
            base.OnConfiguring(optionsBuilder);
        }
    }

To ensure lazy loading, navigation attributes avoid circular references, the need to introduce a package nuget:
Microsoft.EntityFrameworkCore.Proxies
final DbManager codes:

    /// <summary>
    /// 管理数据库的连接与实体的映射
    /// </summary>
    public class DbManager:DbContext
    {
        //数据库连接字符串
        private string? _connectString;
        public DbManager(string connectString)
        {
            _connectString = connectString;
        }
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.ApplyConfigurationsFromAssembly(typeof(DbManager).Assembly);
            base.OnModelCreating(modelBuilder);
        }
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (_connectString!=null)
            {
                optionsBuilder.UseNpgsql(_connectString);
            }
            //确认启用延迟加载,以免自动加载导航属性从而循环引用问题
            optionsBuilder.UseLazyLoadingProxies();
            base.OnConfiguring(optionsBuilder);
        }
    }

OK, now we have an entity, database configuration is also the line, we are still an external call the class, became known as IDbMapper and DbMapper it. (What? Why are there two? Hey, since dotnet core is generally injected into ways to provide services)
(first wave to synchronize da)

     /// <summary>
    /// 对外的数据层访问接口
    /// </summary>
    public interface IDbMapper
    {
        /// <summary>
        /// 向数据库中加入实体数据
        /// </summary>
        /// <typeparam name="TEntity">待加入的实体类型</typeparam>
        /// <param name="entity">待加入的实体数据</param>
        /// <returns>被追踪的实体对象</returns>
        TEntity? Add<TEntity>(TEntity entity) where TEntity : class, new();
        /// <summary>
        /// 删除数据库中的实体数据
        /// </summary>
        /// <typeparam name="TEntity">待删除的实体类型</typeparam>
        /// <param name="entity">待删除的实体数据</param>
        /// <returns>被删除实体数据</returns>
        TEntity? Delete<TEntity>(TEntity entity) where TEntity : class, new();
        /// <summary>
        /// 更新数据库中数据
        /// </summary>
        /// <typeparam name="TEntity">待更新的实体类型</typeparam>
        /// <param name="entity">待更新的实体类型</param>
        /// <returns>返回追踪的实体数据</returns>
        TEntity? Update<TEntity>(TEntity entity) where TEntity : class, new();
        /// <summary>
        /// 获取所有实体数据
        /// </summary>
        /// <typeparam name="TEntity">待获取的实体类型</typeparam>
        /// <returns>数据库中数据列表</returns>
        IEnumerable<TEntity> GetAllEntities<TEntity>() where TEntity : class, new();
        /// <summary>
        /// 获取第一个实体数据
        /// </summary>
        /// <typeparam name="TEntity">待获取的实体类型</typeparam>
        /// <returns>数据库中第一个该类型数据</returns>
        TEntity? GetFirstEntity<TEntity>() where TEntity : class, new();
        /// <summary>
        /// 获取符合条件的数据列表
        /// </summary>
        /// <typeparam name="TEntity">待获取的数据类型</typeparam>
        /// <param name="exp">Lamda条件语句</param>
        /// <returns>符合条件的数据列表</returns>
        IEnumerable<TEntity> GetEntities<TEntity>(Expression<Func<TEntity, bool>> exp) where TEntity : class, new();
        /// <summary>
        /// 获取一个符合条件的数据
        /// </summary>
        /// <typeparam name="TEntity">待获取的数据类型</typeparam>
        /// <param name="exp">Lamda条件语句</param>
        /// <returns>符合条件的数据</returns>
        TEntity? GetEntity<TEntity>(Expression<Func<TEntity, bool>> exp) where TEntity : class, new();
    }

The implementation change DbMapper

    /// <summary>
    /// 数据访问层实现
    /// </summary>
    public class DbMapper:IDbMapper
    {
        protected DbManager dbManager { get; }
        public DbMapper(DbManager dbManager)
        {
            this.dbManager = dbManager;
        }
        protected virtual IEnumerable<TEntity> CompileQuery<TEntity>(Expression<Func<TEntity, bool>> exp) where TEntity : class, new()
        {
            var fn = EF.CompileQuery((DbManager context, Expression<Func<TEntity, bool>> exps) =>
                context.Set<TEntity>().Where(exps));
            return fn(dbManager, exp);
        }

        protected virtual TEntity? CompileQuerySingle<TEntity>(Expression<Func<TEntity, bool>> exp) where TEntity : class, new()
        {
            var fn = EF.CompileQuery((DbManager context, Expression<Func<TEntity, bool>> exps) => context.Set<TEntity>().FirstOrDefault(exps));
            return fn(dbManager, exp);
        }
        public TEntity? Add<TEntity>(TEntity entity) where TEntity : class, new()
        {
            TEntity? result = null;
            try
            {
                var trace = dbManager.Set<TEntity>().Add(entity);
                dbManager.SaveChanges();
                result = trace.Entity;
            }
            catch
            {
                //TODO:记录异常
            }

            return result;
        }

        public TEntity? Delete<TEntity>(TEntity entity) where TEntity : class, new()
        {
            TEntity? result = null;
            try
            {
                var trace = dbManager.Set<TEntity>().Remove(entity);
                dbManager.SaveChanges();
                result = trace.Entity;
            }
            catch
            {
                //TODO:记录异常
            }

            return result;

        }

        public TEntity? Update<TEntity>(TEntity entity) where TEntity : class, new()
        {
            TEntity? result = null;
            try
            {
                var trace = dbManager.Set<TEntity>().Update(entity);
                dbManager.SaveChanges();
                result = trace.Entity;
            }
            catch
            {
                //TODO:记录异常
            }

            return result;

        }
        public IEnumerable<TEntity> GetEntities<TEntity>(Expression<Func<TEntity, bool>> exp) where TEntity : class, new()
        {
            return CompileQuery(exp);
        }

        public TEntity? GetEntity<TEntity>(Expression<Func<TEntity, bool>> exp) where TEntity : class, new()
        {
            return CompileQuerySingle(exp);
        }

        public IEnumerable<TEntity> GetAllEntities<TEntity>() where TEntity : class, new()
        {
            return dbManager.Set<TEntity>().ToList();
        }

        public TEntity? GetFirstEntity<TEntity>() where TEntity : class, new()
        {
            return dbManager.Set<TEntity>().FirstOrDefault();
        }
    }

The final code structure:

Call ~, ~ Finally, we test wave
test method:

        [TestMethod]
        public void DbDataTest()
        {
            var connectString = @"咱的数据库连接字符串~~~";
            var dbManager = new DbManager(connectString);
            var dbMapper = new DbMapper(dbManager);
            //确保数据库被删除啦
            dbManager.Database.EnsureDeleted();
            //确保数据库被新建
            dbManager.Database.EnsureCreated();
            dbMapper.Add(new Author
            {
                Name = "233",
                Account = "233",
                Password = "233",
                LoginTime=DateTime.Now
            });
            var author = dbMapper.GetFirstEntity<Author>();
            Assert.IsTrue(author.Name == "233");
        }
    }

The results are as follows:

data in the database:

Kick knock off ~ ~ ~ ~

Guess you like

Origin www.cnblogs.com/sncufan/p/12394628.html