如何优雅地使用EFCore实例详解进行.NET开发?

2026-03-30 23:521阅读0评论SEO资讯
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1424个文字,预计阅读时间需要6分钟。

目录+ 正文+ DbSet 清除计划+ IEntityTypeConfiguration(表配置)+ Repository(存储)+ Autofac+ 数据库配置+ 项目架构和源码EFCore 是微软官方的ORM框架,主要用于实体和数据库对象之间的操作。主要功能包括:

- DbSet 清除计划:用于管理数据库中的数据操作,如添加、删除、更新等。- IEntityTypeConfiguration(表配置):用于配置数据库表的映射关系。- Repository(存储):提供数据访问层的抽象,简化数据操作。- Autofac:依赖注入框架,用于管理对象的生命周期和依赖关系。- 数据库配置:设置数据库连接信息,如连接字符串、数据库类型等。- 项目架构和源码:展示EFCore在项目中的应用和实现细节。

目录
  • 正文
    • DBSet清除计划
    • IEntityTypeConfiguration(表配置)
    • Repository(仓储)
    • Autofac
    • 数据库配置
    • 项目架构和源码

正文

EFCore是微软官方的一款ORM框架,主要是用于实体和数据库对象之间的操作。功能非常强大,在老版本的时候叫做EF,后来.net core问世,EFCore也随之问世。

本文我们将用一个控制台项目Host一个web服务,并且使用本地Mysql作为数据库,使用EFCore的Code First模式进行数据操作。

DBSet清除计划

以前使用EF/EFCore的开发者应该都记得,需要在DBContext里写好多DBSet,一个表对应一个DBSet,然后在其他地方操作这些DBSet对相关的表进行增删改查。作为一个开发,这些重复操作都是我们希望避免的,我们可以利用反射机制将这些类型通过框架自带的方法循环注册进去。

1.EF实体继承统一的接口,方便我们反射获取所有EF实体,接口可以设置一个泛型,来泛化我们的主键类型,因为可能存在不同的表的主键类型也不一样。

统一的EF实体接口

public interface IEFEntity<TKey> { public TKey Id { get; set; } }

统一的接口实现类

public abstract class AggregateRoot<TKey> : IEFEntity<TKey> { public TKey Id { get; set; } }

用户实体类

public class User : AggregateRoot<string> { public string UserName { get; set; } public DateTime Birthday { get; set; } public virtual ICollection<Book> Books { get; set; } }

2.利用反射获取某个程序集下所有的实体类

public class EFEntityInfo { public (Assembly Assembly, IEnumerable<Type> Types) EFEntitiesInfo => (GetType().Assembly, GetEntityTypes(GetType().Assembly)); private IEnumerable<Type> GetEntityTypes(Assembly assembly) { //获取当前程序集下所有的实现了IEFEntity的实体类 var efEntities = assembly.GetTypes().Where(m => m.FullName != null && Array.Exists(m.GetInterfaces(), t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEFEntity<>)) && !m.IsAbstract && !m.IsInterface).ToArray(); return efEntities; } }

3.DBContext实现类中OnModelCreating方法中注册这些类型

protected override void OnModelCreating(ModelBuilder modelBuilder) { //循环实体类型,并且通过Entity方法注册类型 foreach (var entityType in Types) { modelBuilder.Entity(entityType); } base.OnModelCreating(modelBuilder); }

至此为止所有的实体类都被注册到DBContext中作为DBSets,再也不需要一个个写DBSet了,可以用过DbContext.Set<User>()获取用户的DBSet。

IEntityTypeConfiguration(表配置)

用数据库创建过表的同学都知道,在设计表的时候,可以给表添加很多配置和约束,在Code First模式中,很多同学都是在对象中通过注解的方式配置字段。如下就配置了用户名是不能为NULL和最大长度为500

[Required] [MaxLength(500)] public string UserName { get; set; }

也有的同学在DbContext中的OnModelCreating方法配置

modelBuilder.Entity<User>().Property(x => x.UserName).IsRequired();

这两种方法,前者入侵行太强,直接代码耦合到实体类中了,后者不够清楚,把一大堆表的配置写在一个方法里,当然了很多人说可以拆分不同的方法或者使用注释分开。但是!不够优雅!
我们可以使用IEntityTypeConfiguration接口实现我们所想的优雅的表配置。
1.创建一个配置基类,继承自IEntityTypeConfiguration,做一些通用的配置,比如设置主键,一般都是id啦,还有软删除等。

public abstract class EntityTypeConfiguration<TEntity, TKey> : IEntityTypeConfiguration<TEntity> where TEntity : AggregateRoot<TKey> { public virtual void Configure(EntityTypeBuilder<TEntity> builder) { var entityType = typeof(TEntity); builder.HasKey(x => x.Id); if (typeof(ISoftDelete).IsAssignableFrom(entityType)) { builder.HasQueryFilter(d => EF.Property<bool>(d, "IsDeleted") == false); } } }

2.创建用户实体/表独有的配置,比如设置用户名的最大长度,以及seed一些数据

public class UserConfig : EntityTypeConfiguration<User, string> { public override void Configure(EntityTypeBuilder<User> builder) { base.Configure(builder); builder.Property(x => x.UserName).HasMaxLength(50); //mock一条数据 builder.HasData(new User() { Id = "090213204", UserName = "Bruce", Birthday = DateTime.Parse("1996-08-24") }); } }

当然还有很多配置可以设置,比如索引,导航属性,唯一键等。如下图书实体

public class BookConfig : EntityTypeConfiguration<Book, long> { public override void Configure(EntityTypeBuilder<Book> builder) { base.Configure(builder); builder.Property(x => x.Id).ValueGeneratedOnAdd(); //设置book的id自增 builder.Property(x => x.BookName).HasMaxLength(500).IsRequired(); builder.HasIndex(x => x.Author);//作者添加索引 builder.HasIndex(x => x.SN).IsUnique();//序列号添加唯一索引 builder.HasOne(r => r.User).WithMany(x=>x.Books) .HasForeignKey(r => r.UserId).IsRequired();//导航属性,本质就是创建外键,虽然查询很方便,生产中不建议使用!!! } }

3.DBContext中应用配置

protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.HasCharSet("utf8mb4 "); var (Assembly, Types) = _efEntitysInfo.EFEntitiesInfo; foreach (var entityType in Types) { modelBuilder.Entity(entityType); } //只需要将配置类所在的程序集给到,它会自动加载 modelBuilder.ApplyConfigurationsFromAssembly(Assembly); base.OnModelCreating(modelBuilder); }

Repository(仓储)

这个不过分介绍,特别是基于github.com/BruceQiu1996/EFCoreDemo该项目是一个可以启动运行的基于.net6的控制台项目,启动后会启动一个web host和一个swagger页面。

以上就是.net如何优雅的使用EFCore实例详解的详细内容,更多关于.net使用EFCore的资料请关注自由互联其它相关文章!

本文共计1424个文字,预计阅读时间需要6分钟。

目录+ 正文+ DbSet 清除计划+ IEntityTypeConfiguration(表配置)+ Repository(存储)+ Autofac+ 数据库配置+ 项目架构和源码EFCore 是微软官方的ORM框架,主要用于实体和数据库对象之间的操作。主要功能包括:

- DbSet 清除计划:用于管理数据库中的数据操作,如添加、删除、更新等。- IEntityTypeConfiguration(表配置):用于配置数据库表的映射关系。- Repository(存储):提供数据访问层的抽象,简化数据操作。- Autofac:依赖注入框架,用于管理对象的生命周期和依赖关系。- 数据库配置:设置数据库连接信息,如连接字符串、数据库类型等。- 项目架构和源码:展示EFCore在项目中的应用和实现细节。

目录
  • 正文
    • DBSet清除计划
    • IEntityTypeConfiguration(表配置)
    • Repository(仓储)
    • Autofac
    • 数据库配置
    • 项目架构和源码

正文

EFCore是微软官方的一款ORM框架,主要是用于实体和数据库对象之间的操作。功能非常强大,在老版本的时候叫做EF,后来.net core问世,EFCore也随之问世。

本文我们将用一个控制台项目Host一个web服务,并且使用本地Mysql作为数据库,使用EFCore的Code First模式进行数据操作。

DBSet清除计划

以前使用EF/EFCore的开发者应该都记得,需要在DBContext里写好多DBSet,一个表对应一个DBSet,然后在其他地方操作这些DBSet对相关的表进行增删改查。作为一个开发,这些重复操作都是我们希望避免的,我们可以利用反射机制将这些类型通过框架自带的方法循环注册进去。

1.EF实体继承统一的接口,方便我们反射获取所有EF实体,接口可以设置一个泛型,来泛化我们的主键类型,因为可能存在不同的表的主键类型也不一样。

统一的EF实体接口

public interface IEFEntity<TKey> { public TKey Id { get; set; } }

统一的接口实现类

public abstract class AggregateRoot<TKey> : IEFEntity<TKey> { public TKey Id { get; set; } }

用户实体类

public class User : AggregateRoot<string> { public string UserName { get; set; } public DateTime Birthday { get; set; } public virtual ICollection<Book> Books { get; set; } }

2.利用反射获取某个程序集下所有的实体类

public class EFEntityInfo { public (Assembly Assembly, IEnumerable<Type> Types) EFEntitiesInfo => (GetType().Assembly, GetEntityTypes(GetType().Assembly)); private IEnumerable<Type> GetEntityTypes(Assembly assembly) { //获取当前程序集下所有的实现了IEFEntity的实体类 var efEntities = assembly.GetTypes().Where(m => m.FullName != null && Array.Exists(m.GetInterfaces(), t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEFEntity<>)) && !m.IsAbstract && !m.IsInterface).ToArray(); return efEntities; } }

3.DBContext实现类中OnModelCreating方法中注册这些类型

protected override void OnModelCreating(ModelBuilder modelBuilder) { //循环实体类型,并且通过Entity方法注册类型 foreach (var entityType in Types) { modelBuilder.Entity(entityType); } base.OnModelCreating(modelBuilder); }

至此为止所有的实体类都被注册到DBContext中作为DBSets,再也不需要一个个写DBSet了,可以用过DbContext.Set<User>()获取用户的DBSet。

IEntityTypeConfiguration(表配置)

用数据库创建过表的同学都知道,在设计表的时候,可以给表添加很多配置和约束,在Code First模式中,很多同学都是在对象中通过注解的方式配置字段。如下就配置了用户名是不能为NULL和最大长度为500

[Required] [MaxLength(500)] public string UserName { get; set; }

也有的同学在DbContext中的OnModelCreating方法配置

modelBuilder.Entity<User>().Property(x => x.UserName).IsRequired();

这两种方法,前者入侵行太强,直接代码耦合到实体类中了,后者不够清楚,把一大堆表的配置写在一个方法里,当然了很多人说可以拆分不同的方法或者使用注释分开。但是!不够优雅!
我们可以使用IEntityTypeConfiguration接口实现我们所想的优雅的表配置。
1.创建一个配置基类,继承自IEntityTypeConfiguration,做一些通用的配置,比如设置主键,一般都是id啦,还有软删除等。

public abstract class EntityTypeConfiguration<TEntity, TKey> : IEntityTypeConfiguration<TEntity> where TEntity : AggregateRoot<TKey> { public virtual void Configure(EntityTypeBuilder<TEntity> builder) { var entityType = typeof(TEntity); builder.HasKey(x => x.Id); if (typeof(ISoftDelete).IsAssignableFrom(entityType)) { builder.HasQueryFilter(d => EF.Property<bool>(d, "IsDeleted") == false); } } }

2.创建用户实体/表独有的配置,比如设置用户名的最大长度,以及seed一些数据

public class UserConfig : EntityTypeConfiguration<User, string> { public override void Configure(EntityTypeBuilder<User> builder) { base.Configure(builder); builder.Property(x => x.UserName).HasMaxLength(50); //mock一条数据 builder.HasData(new User() { Id = "090213204", UserName = "Bruce", Birthday = DateTime.Parse("1996-08-24") }); } }

当然还有很多配置可以设置,比如索引,导航属性,唯一键等。如下图书实体

public class BookConfig : EntityTypeConfiguration<Book, long> { public override void Configure(EntityTypeBuilder<Book> builder) { base.Configure(builder); builder.Property(x => x.Id).ValueGeneratedOnAdd(); //设置book的id自增 builder.Property(x => x.BookName).HasMaxLength(500).IsRequired(); builder.HasIndex(x => x.Author);//作者添加索引 builder.HasIndex(x => x.SN).IsUnique();//序列号添加唯一索引 builder.HasOne(r => r.User).WithMany(x=>x.Books) .HasForeignKey(r => r.UserId).IsRequired();//导航属性,本质就是创建外键,虽然查询很方便,生产中不建议使用!!! } }

3.DBContext中应用配置

protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.HasCharSet("utf8mb4 "); var (Assembly, Types) = _efEntitysInfo.EFEntitiesInfo; foreach (var entityType in Types) { modelBuilder.Entity(entityType); } //只需要将配置类所在的程序集给到,它会自动加载 modelBuilder.ApplyConfigurationsFromAssembly(Assembly); base.OnModelCreating(modelBuilder); }

Repository(仓储)

这个不过分介绍,特别是基于github.com/BruceQiu1996/EFCoreDemo该项目是一个可以启动运行的基于.net6的控制台项目,启动后会启动一个web host和一个swagger页面。

以上就是.net如何优雅的使用EFCore实例详解的详细内容,更多关于.net使用EFCore的资料请关注自由互联其它相关文章!