Develop blog project based on abp vNext and. NET Core - data access and code first

Keywords: Database JSON MySQL PostgreSQL

Last article( https://www.cnblogs.com/meowv/p/12909558.html )Improved the code in the project and connected to Swagger. This article mainly uses the Entity Framework Core to access the database, and uses the code first method for data migration and automatic creation of table structure.

data access

Add our data access context object MeowvBlogDbContext in the. EntityFrameworkCore project, which inherits from abpdbcontext < T >. Then override the OnModelCreating method.

Onmodelcreating: defines EF Core entity mapping. Call first base.OnModelCreating Let the abp framework implement the basic mapping for us and then call it. builder.Configure() extends the method to configure the application's entities. Of course, it can also be written directly in it without expansion. Such a big lump is not good-looking.

In the abp framework, we can use the [ConnectionStringName] Attribute to configure the connection string name for our DbContext. Add first, then appsettings.json Because multiple databases have been integrated before, we also configure multiple connection strings corresponding to them.

This project uses MySql by default. You can choose whatever you like.

//MeowvBlogDbContext.cs
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;

namespace Meowv.Blog.EntityFrameworkCore
{
    [ConnectionStringName("MySql")]
    public class MeowvBlogDbContext : AbpDbContext<MeowvBlogDbContext>
    {
        public MeowvBlogDbContext(DbContextOptions<MeowvBlogDbContext> options) : base(options)
        {
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            modelBuilder.Configure();
        }
    }
}
//appsettings.json
{
  "ConnectionStrings": {
    "Enable": "MySQL",
    "MySql": "Server=localhost;User Id=root;Password=123456;Database=meowv_blog_tutorial",
    "SqlServer": "",
    "PostgreSql": "",
    "Sqlite": ""
  }
}

Then create our extension class meowvblogdbcontextmod elCreatingExtensions.cs And extension method Configure(). Note that the extension method is static, and static should be added

//MeowvBlogDbContextModelCreatingExtensions.cs
using Microsoft.EntityFrameworkCore;
using Volo.Abp;

namespace Meowv.Blog.EntityFrameworkCore
{
    public static class MeowvBlogDbContextModelCreatingExtensions
    {
        public static void Configure(this ModelBuilder builder)
        {
            Check.NotNull(builder, nameof(builder));
            ...
        }
    }
}

After completing the above operations, register DbContext to dependency injection in our module class MeowvBlogFrameworkCoreModule, and use different databases according to your configured values. Create a profile access class at the. Domain layer AppSettings.cs

//AppSettings.cs
using Microsoft.Extensions.Configuration;
using System.IO;

namespace Meowv.Blog.Domain.Configurations
{
    /// <summary>
    /// appsettings.json Profile data reading class
    /// </summary>
    public class AppSettings
    {
        /// <summary>
        ///Root node of profile
        /// </summary>
        private static readonly IConfigurationRoot _config;

        /// <summary>
        /// Constructor
        /// </summary>
        static AppSettings()
        {
            // load appsettings.json , and build IConfigurationRoot
            var builder = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory())
                                                    .AddJsonFile("appsettings.json", true, true);
            _config = builder.Build();
        }

        /// <summary>
        /// EnableDb
        /// </summary>
        public static string EnableDb => _config["ConnectionStrings:Enable"];

        /// <summary>
        /// ConnectionStrings
        /// </summary>
        public static string ConnectionStrings => _config.GetConnectionString(EnableDb);
    }
}

It's easy to get the contents of the configuration file, and it's easy to understand the comments in the code.

It is worth mentioning that ABP will automatically create a default repository for the entities in DbContext. You need to use options to add AddDefaultRepositories() when registering.

By default, a warehouse is created for each entity. If you want to create a warehouse for other entities, you can set includeAllEntities to true, and then you can inject and use irepository < content > or iqueryablerepository < content > in the service

//MeowvBlogFrameworkCoreModule.cs
using Meowv.Blog.Domain;
using Meowv.Blog.Domain.Configurations;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.MySQL;
using Volo.Abp.EntityFrameworkCore.PostgreSql;
using Volo.Abp.EntityFrameworkCore.Sqlite;
using Volo.Abp.EntityFrameworkCore.SqlServer;
using Volo.Abp.Modularity;

namespace Meowv.Blog.EntityFrameworkCore
{
    [DependsOn(
        typeof(MeowvBlogDomainModule),
        typeof(AbpEntityFrameworkCoreModule),
        typeof(AbpEntityFrameworkCoreMySQLModule),
        typeof(AbpEntityFrameworkCoreSqlServerModule),
        typeof(AbpEntityFrameworkCorePostgreSqlModule),
        typeof(AbpEntityFrameworkCoreSqliteModule)
    )]
    public class MeowvBlogFrameworkCoreModule : AbpModule
    {
        public override void ConfigureServices(ServiceConfigurationContext context)
        {
            context.Services.AddAbpDbContext<MeowvBlogDbContext>(options =>
            {
                options.AddDefaultRepositories(includeAllEntities: true);
            });

            Configure<AbpDbContextOptions>(options =>
            {
                switch (AppSettings.EnableDb)
                {
                    case "MySQL":
                        options.UseMySQL();
                        break;
                    case "SqlServer":
                        options.UseSqlServer();
                        break;
                    case "PostgreSql":
                        options.UsePostgreSql();
                        break;
                    case "Sqlite":
                        options.UseSqlite();
                        break;
                    default:
                        options.UseMySQL();
                        break;
                }
            });
        }
    }
}

Now, we can preliminarily design the required tables of Blog: posts, categories, tags, posts_ tags), friendlinks

Write Entity classes in the. Domain layer, Post.cs , Category.cs , Tag.cs , PostTag.cs , FriendLink.cs . Set the primary key to int type and directly inherit the Entity. Refer to ABP documentation for this, https://docs.abp.io/zh-Hans/abp/latest/Entities

Click to view the code
//Post.cs
using System;
using Volo.Abp.Domain.Entities;

namespace Meowv.Blog.Domain.Blog
{
    /// <summary>
    /// Post
    /// </summary>
    public class Post : Entity<int>
    {
        /// <summary>
        ///Title
        /// </summary>
        public string Title { get; set; }

        /// <summary>
        ///Author
        /// </summary>
        public string Author { get; set; }

        /// <summary>
        ///Links
        /// </summary>
        public string Url { get; set; }

        /// <summary>
        /// HTML
        /// </summary>
        public string Html { get; set; }

        /// <summary>
        /// Markdown
        /// </summary>
        public string Markdown { get; set; }

        /// <summary>
        ///Classification Id
        /// </summary>
        public int CategoryId { get; set; }

        /// <summary>
        ///Creation time
        /// </summary>
        public DateTime CreationTime { get; set; }
    }
}
//Category.cs
using Volo.Abp.Domain.Entities;

namespace Meowv.Blog.Domain.Blog
{
    /// <summary>
    /// Category
    /// </summary>
    public class Category : Entity<int>
    {
        /// <summary>
        ///Classification name
        /// </summary>
        public string CategoryName { get; set; }

        /// <summary>
        ///Display name
        /// </summary>
        public string DisplayName { get; set; }
    }
}
//Tag.cs
using Volo.Abp.Domain.Entities;

namespace Meowv.Blog.Domain.Blog
{
    /// <summary>
    /// Tag
    /// </summary>
    public class Tag : Entity<int>
    {
        /// <summary>
        ///Label name
        /// </summary>
        public string TagName { get; set; }

        /// <summary>
        ///Display name
        /// </summary>
        public string DisplayName { get; set; }
    }
}
//PostTag.cs
using Volo.Abp.Domain.Entities;

namespace Meowv.Blog.Domain.Blog
{
    /// <summary>
    /// PostTag
    /// </summary>
    public class PostTag : Entity<int>
    {
        /// <summary>
        ///Article Id
        /// </summary>
        public int PostId { get; set; }

        /// <summary>
        ///Tag Id
        /// </summary>
        public int TagId { get; set; }
    }
}
//FriendLink.cs
using Volo.Abp.Domain.Entities;

namespace Meowv.Blog.Domain.Blog
{
    /// <summary>
    /// FriendLink
    /// </summary>
    public class FriendLink : Entity<int>
    {
        /// <summary>
        ///Title
        /// </summary>
        public string Title { get; set; }

        /// <summary>
        ///Links
        /// </summary>
        public string LinkUrl { get; set; }
    }
}

After creating entity class, add DbSet property in MeowvBlogDbContext

//MeowvBlogDbContext.cs
...
    [ConnectionStringName("MySql")]
    public class MeowvBlogDbContext : AbpDbContext<MeowvBlogDbContext>
    {
        public DbSet<Post> Posts { get; set; }

        public DbSet<Category> Categories { get; set; }

        public DbSet<Tag> Tags { get; set; }

        public DbSet<PostTag> PostTags { get; set; }

        public DbSet<FriendLink> FriendLinks { get; set; }

        ...
    }
...

At Domain.Shared Layer add global constant class MeowvBlogConsts.cs And table name constant classes MeowvBlogDbConsts.cs Let's make a table prefix constant. I'm writing meowv here_ , you can do whatever you like. Our table names will be meowv_ start. Then define the table name in meowvblogdbcomts.

//MeowvBlogConsts.cs
namespace Meowv.Blog.Domain.Shared
{
    /// <summary>
    ///Global constant
    /// </summary>
    public class MeowvBlogConsts
    {
        /// <summary>
        ///Database table prefix
        /// </summary>
        public const string DbTablePrefix = "meowv_";
    }
}
//MeowvBlogDbConsts.cs
namespace Meowv.Blog.Domain.Shared
{
    public class MeowvBlogDbConsts
    {
        public static class DbTableName
        {
            public const string Posts = "Posts";

            public const string Categories = "Categories";

            public const string Tags = "Tags";

            public const string PostTags = "Post_Tags";

            public const string Friendlinks = "Friendlinks";
        }
    }
}

Configure the table model in the Configure() method, including table name, field type, length and other information. For the following code that is not very clear, you can see the Microsoft custom Code First convention: https://docs.microsoft.com/zh-cn/ef/ef6/modeling/code-first/conventions/custom

//MeowvBlogDbContextModelCreatingExtensions.cs
using Meowv.Blog.Domain.Blog;
using Meowv.Blog.Domain.Shared;
using Microsoft.EntityFrameworkCore;
using Volo.Abp;
using static Meowv.Blog.Domain.Shared.MeowvBlogDbConsts;

namespace Meowv.Blog.EntityFrameworkCore
{
    public static class MeowvBlogDbContextModelCreatingExtensions
    {
        public static void Configure(this ModelBuilder builder)
        {
            Check.NotNull(builder, nameof(builder));

            builder.Entity<Post>(b =>
            {
                b.ToTable(MeowvBlogConsts.DbTablePrefix + DbTableName.Posts);
                b.HasKey(x => x.Id);
                b.Property(x => x.Title).HasMaxLength(200).IsRequired();
                b.Property(x => x.Author).HasMaxLength(10);
                b.Property(x => x.Url).HasMaxLength(100).IsRequired();
                b.Property(x => x.Html).HasColumnType("longtext").IsRequired();
                b.Property(x => x.Markdown).HasColumnType("longtext").IsRequired();
                b.Property(x => x.CategoryId).HasColumnType("int");
                b.Property(x => x.CreationTime).HasColumnType("datetime");
            });

            builder.Entity<Category>(b =>
            {
                b.ToTable(MeowvBlogConsts.DbTablePrefix + DbTableName.Categories);
                b.HasKey(x => x.Id);
                b.Property(x => x.CategoryName).HasMaxLength(50).IsRequired();
                b.Property(x => x.DisplayName).HasMaxLength(50).IsRequired();
            });

            builder.Entity<Tag>(b =>
            {
                b.ToTable(MeowvBlogConsts.DbTablePrefix + DbTableName.Tags);
                b.HasKey(x => x.Id);
                b.Property(x => x.TagName).HasMaxLength(50).IsRequired();
                b.Property(x => x.DisplayName).HasMaxLength(50).IsRequired();
            });

            builder.Entity<PostTag>(b =>
            {
                b.ToTable(MeowvBlogConsts.DbTablePrefix + DbTableName.PostTags);
                b.HasKey(x => x.Id);
                b.Property(x => x.PostId).HasColumnType("int").IsRequired();
                b.Property(x => x.TagId).HasColumnType("int").IsRequired();
            });

            builder.Entity<FriendLink>(b =>
            {
                b.ToTable(MeowvBlogConsts.DbTablePrefix + DbTableName.Friendlinks);
                b.HasKey(x => x.Id);
                b.Property(x => x.Title).HasMaxLength(20).IsRequired();
                b.Property(x => x.LinkUrl).HasMaxLength(100).IsRequired();
            });
        }
    }
}

At this time, the project level directory is as follows

Code first

At EntityFrameworkCore.DbMigrations New module class meowvblogentityframeworkco in reDbMigrationsModule.cs , data migration context access object meowvblo gMigrationsDbContext.cs And a Design Time Db Factory class meowvblogmigrate ionsDbContextFactory.cs

The module class depends on the MeowvBlogFrameworkCoreModule module and AbpModule. And add dependency injection of context in the ConfigureServices method.

//MeowvBlogEntityFrameworkCoreDbMigrationsModule.cs
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Modularity;

namespace Meowv.Blog.EntityFrameworkCore.DbMigrations.EntityFrameworkCore
{
    [DependsOn(
        typeof(MeowvBlogFrameworkCoreModule)
    )]
    public class MeowvBlogEntityFrameworkCoreDbMigrationsModule : AbpModule
    {
        public override void ConfigureServices(ServiceConfigurationContext context)
        {
            context.Services.AddAbpDbContext<MeowvBlogMigrationsDbContext>();
        }
    }
}

There is no big difference between MeowvBlogMigrationsDbContext and MeowvBlogDbContext

//MeowvBlogMigrationsDbContext.cs
using Microsoft.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;

namespace Meowv.Blog.EntityFrameworkCore.DbMigrations.EntityFrameworkCore
{
    public class MeowvBlogMigrationsDbContext : AbpDbContext<MeowvBlogMigrationsDbContext>
    {
        public MeowvBlogMigrationsDbContext(DbContextOptions<MeowvBlogMigrationsDbContext> options) : base(options)
        {

        }

        protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);

            builder.Configure();
        }
    }
}

The MeowvBlogMigrationsDbContextFactory class is mainly used to use the code first command (add migration and update database...)

Note that we need to set the connection string of the configuration file separately HttpApi.Hosting Stratified appsettings.json Make a copy to EntityFrameworkCore.DbMigrations , you can configure the connection string of any database you use.

//appsettings.json
{
  "ConnectionStrings": {
    "Default": "Server=localhost;User Id=root;Password=123456;Database=meowv_blog"
  }
}
//MeowvBlogMigrationsDbContextFactory.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Extensions.Configuration;
using System.IO;

namespace Meowv.Blog.EntityFrameworkCore.DbMigrations.EntityFrameworkCore
{
    public class MeowvBlogMigrationsDbContextFactory : IDesignTimeDbContextFactory<MeowvBlogMigrationsDbContext>
    {
        public MeowvBlogMigrationsDbContext CreateDbContext(string[] args)
        {
            var configuration = BuildConfiguration();

            var builder = new DbContextOptionsBuilder<MeowvBlogMigrationsDbContext>()
                .UseMySql(configuration.GetConnectionString("Default"));

            return new MeowvBlogMigrationsDbContext(builder.Options);
        }

        private static IConfigurationRoot BuildConfiguration()
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);

            return builder.Build();
        }
    }
}

It's almost over here. The default database is meowv_blog_tutorial does not exist. First, create an empty database.

Then open the package management console in Visual Studio, and set EntityFrameworkCore.DbMigrations Set to start project.

Type the command: add migration initial, and you will find that the error message is as follows:

Add migration: the add migration entry cannot be recognized as the name of a cmdlet, function, script file, or runnable program. Check the spelling of the name. If you include a path, make sure the path is correct, and then try again.
Location line: 1 character: 1
+ Add-Migration Initial
+ ~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (Add-Migration:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException
 

This is because we have added one less package. In order to migrate data using code first method, we must add, Microsoft.EntityFrameworkCore.Tools .

Then install package directly with the command Microsoft.EntityFrameworkCore.Tools Bag, try again

You can see that it has been successful, and a Migrations folder and corresponding data migration files have been generated

Finally, enter the update command: update database, and then open the data view.

Perfect, successfully created the database table, and the name is what we want, and the field type is ok__ The migration history table is used to record the migration history, which can be ignored. When we want to modify and add table fields and tables later, we can use this method.

Solution level catalog for reference

Have you learned how to use Entity Framework Core to complete data access and code first to create database tables? 😁😁😁

Open source address: https://github.com/Meowv/Blog/tree/blog_tutorial

Posted by Monshery on Wed, 20 May 2020 02:56:28 -0700