当前位置: 代码迷 >> 综合 >> 【三】Asp.NetCore使用Efcore+Mysql实现CodeFirst@Asp.netcore Code First +DDD学习笔记
  详细解决方案

【三】Asp.NetCore使用Efcore+Mysql实现CodeFirst@Asp.netcore Code First +DDD学习笔记

热度:11   发布时间:2023-12-26 19:07:33.0

Asp.NetCore使用Efcore+Mysql实现CodeFirst,并且自动生成EfCore的builderModel的实现过程

主题:Asp.netcore Code First +DDD学习笔记

目录:

   【一】 CodeFirst+DDD项目结构的构建

   【二】Asp.Netcore使用Panda.DynamicWebApi来进行Controller解耦

   【三】Asp.NetCore使用Efcore+Mysql实现CodeFirst

   【四】EfCore实现全自动化迁移

     案例代码下载点击

Domain和数据持久化的实现,Domain和Controller之间的链接本来就是可隔离的,所以直接分开就可以了,通过orm进行对数据的控制和持久化,这里采用Efcore+mysql实现codefirst来进行实现。

Domain和Efcore之间我们也不能过度耦合,所以这里不采用传统的手写efcontent,这里采用特性和反射来实现自动生成efcontent,链接数据库采用的是Asp mvc依赖注入。

Domain和Dto之间的映射。

1、EfModel特性的实现

      在EfCoreRepository项目下添加一个文件夹命名为EfModelAttributes和一个类命名为EfEntityBuilder类、在EfModelAttributes该文件夹下添加一个类命名为EfModelAttribute,如下图:

EfModelAttribute代码的实现

using System;namespace EfCoreRepository.EfModelAttributes
{/// <summary>/// EFmodel的实体特性/// 作者:猴子 2019-09-19/// </summary>[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]public class EfModelAttribute: Attribute{}
}

EfEntityBuilder代码的实现

using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
using System.Reflection;namespace EfCoreRepository
{/// <summary>/// ef实体的创建/// </summary>public static class EfEntityBuilder{/// <summary>/// 从程序反射添加实体配置/// </summary>/// <typeparam name="TAttribute"></typeparam>/// <param name="modelBuilder"></param>/// <param name="assembly"></param>public static void AddEntityConfigurationsFromAssembly<TAttribute>(this ModelBuilder modelBuilder, Assembly assembly)where TAttribute : Attribute{var autoTypes = assembly.GetTypes().Where(x => x.GetCustomAttribute<TAttribute>() != null && x.IsPublic);foreach (var entity in autoTypes){modelBuilder.Model.AddEntityType(entity);}foreach (var entity in modelBuilder.Model.GetEntityTypes()){var currentTableName = modelBuilder.Entity(entity.Name).Metadata.Relational().TableName;modelBuilder.Entity(entity.Name).ToTable(currentTableName.ToLower());var properties = entity.GetProperties();foreach (var property in properties)modelBuilder.Entity(entity.Name).Property(property.Name).HasColumnName(property.Name.ToLower());}}}
}

2、Domain里面的Efcontent的实现

     在Domain这个项目里面添加三个文件夹分别命名为:CoreEntity(存放实体基类的),Domains(存放实体的),EfCoreContent(存放efcorecontent的)。在EfCoreContent下添加一个EfContent类,如下图:

     

EfContent的代码实现:

using EfCoreRepository;
using EfCoreRepository.EfModelAttributes;
using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;namespace Domain.EfCoreContent
{public class EfContent : DbContext{public EfContent(DbContextOptions<EfContent> options) : base(options){//部署时需要创建一个数据库实体版本管理表//每次需要执行创建迁移命令Add-Migration InitialCreate//执行前需要创建该表//CREATE TABLE `__EFMigrationsHistory` //(//    `MigrationId` nvarchar(150) NOT NULL,//    `ProductVersion` nvarchar(32) NOT NULL,//     PRIMARY KEY(`MigrationId`)//);if (base.Database.GetPendingMigrations().Any()){base.Database.Migrate(); //执行迁移} }/ <summary>/ 因为使用无参构造器,所以需要自带链接字符串/ </summary>/ <param name="optionsBuilder"></param>//protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)//{//    if (!optionsBuilder.IsConfigured)//        optionsBuilder.UseMySQL("Database = 'studyddd'; Data Source = 'localhost'; User Id = 'root'; Password = ''; charset = 'utf8'; pooling = true; Allow Zero Datetime = True;Allow User Variables=True;TreatTinyAsBoolean=false");//} /// <summary>/// 创建实体/// </summary>/// <param name="modelBuilder"></param>protected override void OnModelCreating(ModelBuilder modelBuilder){modelBuilder.AddEntityConfigurationsFromAssembly<EfModelAttribute>(Assembly.GetExecutingAssembly());base.OnModelCreating(modelBuilder);}/// <summary>/// 扩展查询/// </summary>/// <typeparam name="T"></typeparam>/// <param name="predicate"></param>/// <returns></returns>public IQueryable<T> Where<T>(Expression<Func<T, bool>> predicate) where T : class{return base.Set<T>().Where(predicate);}}
}

  3、在Api项目里面注入Efcontent

        在Api的Startup文件里面的ConfigureServices方法添加如下代码

services.AddDbContextPool<EfContent>(options =>options.UseMySQL("Database = 'studyddd'; Data Source = 'localhost'; User Id = 'root'; Password = ''; charset = 'utf8'; pooling = true; Allow Zero Datetime = True;Allow User Variables=True;TreatTinyAsBoolean=false",b=>b.MigrationsAssembly("Api")), 200);

 这时候,自动生成Efcontent已经完成。

4、创建一个实体进行测试

 在Domain项目下的Domains文件夹里面创建Tests文件夹,在该文件夹下创建Test类如下

Test代码实现如下:

using EfCoreRepository.EfModelAttributes;
using System.ComponentModel.DataAnnotations;namespace Domain.Domains.Tests
{/// <summary>/// 测试类/// </summary>[EfModel]public class Test {[Key]public int Id { get; set; }public string HelloWorld { get; set; }}
}

注:这里的实体要打上刚刚我们做的EfModel特性,不打特性的不会被自动创建到Efcontent里面。

5、在Controller里面开始使用Test该实体进行数据存储和访问。

      修改Controller项目里面的TestAppService这个类里面如下

using Controller.Core;
using Domain.EfCoreContent;
using System.Linq;namespace Controller.Controllers.Test
{/// <summary>/// 测试类/// </summary>public class TestAppService : AppService, IAppService{public EfContent EfContent { get; set; }public TestAppService(EfContent efContent){EfContent = efContent;}public string GetHelloWorld(){return "Hello World !!";}/// <summary>/// 添加一条测试数据/// </summary>/// <returns></returns>public string AddTest(){Domain.Domains.Tests.Test test = new Domain.Domains.Tests.Test();test.HelloWorld="你好世界";EfContent.Add(test);EfContent.SaveChanges();return "写入成功";}/// <summary>/// 获取一条数据/// </summary>/// <returns></returns>public Domain.Domains.Tests.Test GetTest(){return EfContent.Where<Domain.Domains.Tests.Test>(p => p.HelloWorld == "你好世界").FirstOrDefault(); ;}}
}

我实现了一个添加数据的接口,和一个查询数据的接口。

当然这时候我们还不能之间运行项目开始测试,这时候我们需要到数据库创建一个数据库版本管理表,运行如下sql

CREATE TABLE `__EFMigrationsHistory` 
(
  `MigrationId` nvarchar(150) NOT NULL,
  `ProductVersion` nvarchar(32) NOT NULL,
   PRIMARY KEY(`MigrationId`)
);

生成表后如图:

 

之后如果我们实体进行了修改或者调整,需要在【程序包管理控制台里面执行迁移命令】,每次修改或者添加都需要执行创建迁移命令

 Add-Migration InitialCreate

注:程序包管理器执行命令时,要选择Api项目,如图

如何打开程序包管理控制台

  

执行命令成功后,你会发现在Api项目里面多了一个文件夹,如图:

该文件夹里面的内容不用管他,开始启动项目Api项目,打开swagger你会发现多了两个接口和一个Models,如下图

 

执行一次添加一条测试数据的接口,你会发现数据库里面自动多了一个test表,如下图

数据库:

执行下 查询接口:

到这里EfCore+Mysql  CodeFirst方式已经实现。

6、Dto的使用:

     Dto主要作用是控制返回和接入的数据,所以他和别的模块并没有一些很强的交合,主要就是一些实体。我们在Dto里面创建一个testdto如下图

TestDto的代码实现如下

namespace Dto.TestDtos
{public class TestDto{public string HelloWorld { get; set; } }
}

在Controller里面的使用,修改获取一条数据的接口如下:

        /// <summary>/// 获取一条数据/// </summary>/// <returns></returns>public Dto.TestDtos.TestDto GetTest(){return EntityToDto<Dto.TestDtos.TestDto>(EfContent.Where<Domain.Domains.Tests.Test>(p => p.HelloWorld == "你好世界").FirstOrDefault()); }

这里使用之前在基类里面封装的一个实体到Dto的方法。

再次运行Api项目,使用Swagger执行 获取一条数据的接口会发现返回的数据Id消失了,如下:

 

总结:这里我发现该CodeFirst方式,有个美中不足的就是结构迁移,需要执行一下迁移命令,还不能全部代码化,后续需要进行优化,如果有大神有建议和方法,请大神不吝赐教。

github地址:https://github.com/houliren/Asp.netcore-Code-First-DDD

  相关解决方案