设为首页收藏本站
网站公告 | 这是第一条公告
     

 找回密码
 立即注册
缓存时间00 现在时间00 缓存数据 你是我生命中所能经历的,最最深切的感觉。

你是我生命中所能经历的,最最深切的感觉。

查看: 1472|回复: 2

.net如何优雅的使用EFCore实例详解

[复制链接]

  离线 

TA的专栏

  • 打卡等级:热心大叔
  • 打卡总天数:203
  • 打卡月天数:0
  • 打卡总奖励:3078
  • 最近打卡:2023-08-27 01:50:51
等级头衔

等級:晓枫资讯-上等兵

在线时间
0 小时

积分成就
威望
0
贡献
429
主题
386
精华
0
金钱
4325
积分
835
注册时间
2022-12-26
最后登录
2025-3-12

发表于 2023-2-26 20:37:58 | 显示全部楼层 |阅读模式

正文

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

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

DBSet清除计划

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

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

统一的EF实体接口

  1. public interface IEFEntity<TKey>
  2. {
  3. public TKey Id { get; set; }
  4. }
复制代码

统一的接口实现类

  1. public abstract class AggregateRoot<TKey> : IEFEntity<TKey>
  2. {
  3. public TKey Id { get; set; }
  4. }
复制代码

用户实体类

  1. public class User : AggregateRoot<string>
  2. {
  3. public string UserName { get; set; }
  4. public DateTime Birthday { get; set; }
  5. public virtual ICollection<Book> Books { get; set; }
  6. }
复制代码

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

  1. public class EFEntityInfo
  2. {
  3. public (Assembly Assembly, IEnumerable<Type> Types) EFEntitiesInfo => (GetType().Assembly, GetEntityTypes(GetType().Assembly));
  4. private IEnumerable<Type> GetEntityTypes(Assembly assembly)
  5. {
  6. //获取当前程序集下所有的实现了IEFEntity的实体类
  7. var efEntities = assembly.GetTypes().Where(m => m.FullName != null
  8. && Array.Exists(m.GetInterfaces(), t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEFEntity<>))
  9. && !m.IsAbstract && !m.IsInterface).ToArray();
  10. return efEntities;
  11. }
  12. }
复制代码

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

  1. protected override void OnModelCreating(ModelBuilder modelBuilder)
  2. {
  3. //循环实体类型,并且通过Entity方法注册类型
  4. foreach (var entityType in Types)
  5. {
  6. modelBuilder.Entity(entityType);
  7. }
  8. base.OnModelCreating(modelBuilder);
  9. }
复制代码

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

IEntityTypeConfiguration(表配置)

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

  1. [Required]
  2. [MaxLength(500)]
  3. public string UserName { get; set; }
复制代码

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

  1. modelBuilder.Entity<User>().Property(x => x.UserName).IsRequired();
复制代码

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

  1. public abstract class EntityTypeConfiguration<TEntity, TKey> : IEntityTypeConfiguration<TEntity>
  2. where TEntity : AggregateRoot<TKey>
  3. {
  4. public virtual void Configure(EntityTypeBuilder<TEntity> builder)
  5. {
  6. var entityType = typeof(TEntity);
  7. builder.HasKey(x => x.Id);
  8. if (typeof(ISoftDelete).IsAssignableFrom(entityType))
  9. {
  10. builder.HasQueryFilter(d => EF.Property<bool>(d, "IsDeleted") == false);
  11. }
  12. }
  13. }
复制代码

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

  1. public class UserConfig : EntityTypeConfiguration<User, string>
  2. {
  3. public override void Configure(EntityTypeBuilder<User> builder)
  4. {
  5. base.Configure(builder);
  6. builder.Property(x => x.UserName).HasMaxLength(50);
  7. //mock一条数据
  8. builder.HasData(new User()
  9. {
  10. Id = "090213204",
  11. UserName = "Bruce",
  12. Birthday = DateTime.Parse("1996-08-24")
  13. });
  14. }
  15. }
复制代码

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

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

3.DBContext中应用配置

  1. protected override void OnModelCreating(ModelBuilder modelBuilder)
  2. {
  3. modelBuilder.HasCharSet("utf8mb4 ");
  4. var (Assembly, Types) = _efEntitysInfo.EFEntitiesInfo;
  5. foreach (var entityType in Types)
  6. {
  7. modelBuilder.Entity(entityType);
  8. }
  9. //只需要将配置类所在的程序集给到,它会自动加载
  10. modelBuilder.ApplyConfigurationsFromAssembly(Assembly);
  11. base.OnModelCreating(modelBuilder);
  12. }
复制代码

Repository(仓储)

这个不过分介绍,特别是基于http的微服务中基本都有这个。

1.创建一个仓储基类,对于不同的实体,创建一样的增删改查方法。

简单写几个查询的方法定义。

  1. public interface IAsyncRepository<TEntity, Tkey> where TEntity : class
  2. {
  3. IQueryable<TEntity> All();
  4. IQueryable<TEntity> All(string[] propertiesToInclude);
  5. IQueryable<TEntity> Where(Expression<Func<TEntity, bool>> filter);
  6. IQueryable<TEntity> Where(Expression<Func<TEntity, bool>> filter, string[] propertiesToInclude);
  7. }
复制代码

2.创建仓储实现类,将DBContext注入到构造中

  1. public class GenericRepository<TEntity, Tkey> : IAsyncRepository<TEntity, Tkey> where TEntity : class
  2. {
  3. protected readonly LibraryDbContext _dbContext;
  4. public GenericRepository(LibraryDbContext dbContext)
  5. {
  6. _dbContext = dbContext;
  7. }
  8. ~GenericRepository()
  9. {
  10. _dbContext?.Dispose();
  11. }
  12. public virtual IQueryable<TEntity> All()
  13. {
  14. return All(null);
  15. }
  16. public virtual IQueryable<TEntity> All(string[] propertiesToInclude)
  17. {
  18. var query = _dbContext.Set<TEntity>().AsNoTracking();
  19. if (propertiesToInclude != null)
  20. {
  21. foreach (var property in propertiesToInclude.Where(p => !string.IsNullOrWhiteSpace(p)))
  22. {
  23. query = query.Include(property);
  24. }
  25. }
  26. return query;
  27. }
  28. }
复制代码

Autofac

1.注入DBContext到Repository的构造方法中,并且注入Repository

  1. public class EFCoreEleganceUseEFCoreModule : Module
  2. {
  3. protected override void Load(ContainerBuilder builder)
  4. {
  5. base.Load(builder);
  6. builder.RegisterModule<EFCoreEleganceUseDomainModule>(); //注入domain模块
  7. builder.RegisterGeneric(typeof(GenericRepository<,>))//将dbcontext注入到仓储的构造中
  8. .UsingConstructor(typeof(LibraryDbContext))
  9. .AsImplementedInterfaces()
  10. .InstancePerDependency();
  11. builder.RegisterType<WorkUnit>().As<IWorkUnit>().InstancePerDependency();
  12. }
  13. }
复制代码

2.Domain注入EFEntityInfo

  1. public class EFCoreEleganceUseDomainModule : Module
  2. {
  3. protected override void Load(ContainerBuilder builder)
  4. {
  5. builder.RegisterType<EFEntityInfo>().SingleInstance();
  6. }
  7. }
复制代码

数据库配置

1.注入DBContext,从配置文件读取数据库配置,然后根据开发/生产环境做一些特殊处理

  1. var mysqlConfig = hostContext.Configuration.GetSection("Mysql").Get<MysqlOptions>();
  2. var serverVersion = new MariaDbServerVersion(new Version(mysqlConfig.Version));
  3. services.AddDbContextFactory<LibraryDbContext>(options =>
  4. {
  5. options.UseMySql(mysqlConfig.ConnectionString, serverVersion, optionsBuilder =>
  6. {
  7. optionsBuilder.MinBatchSize(4);
  8. optionsBuilder.CommandTimeout(10);
  9. optionsBuilder.MigrationsAssembly(mysqlConfig.MigrationAssembly);//迁移文件所在的程序集
  10. optionsBuilder.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery);
  11. }).UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
  12. //开发环境可以打开日志记录和显示详细的错误
  13. if (hostContext.HostingEnvironment.IsDevelopment())
  14. {
  15. options.EnableSensitiveDataLogging();
  16. options.EnableDetailedErrors();
  17. }
  18. });
复制代码

项目架构和源码

213856s95hp55eecbvhhpx.png

项目只是一个demo架构,并不适用于生产,主程序是一个控制台项目,只需要引用相关的包和模块,就可以启动一个web host.

全部代码已经全部上传到github:https://github.com/BruceQiu1996/EFCoreDemo该项目是一个可以启动运行的基于.net6的控制台项目,启动后会启动一个web host和一个swagger页面。

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


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
晓枫资讯-科技资讯社区-免责声明
免责声明:以上内容为本网站转自其它媒体,相关信息仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同其观点或证实其内容的真实性。
      1、注册用户在本社区发表、转载的任何作品仅代表其个人观点,不代表本社区认同其观点。
      2、管理员及版主有权在不事先通知或不经作者准许的情况下删除其在本社区所发表的文章。
      3、本社区的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,举报反馈:点击这里给我发消息进行删除处理。
      4、本社区一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
      5、以上声明内容的最终解释权归《晓枫资讯-科技资讯社区》所有。
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~

  离线 

TA的专栏

等级头衔

等級:晓枫资讯-列兵

在线时间
0 小时

积分成就
威望
0
贡献
0
主题
0
精华
0
金钱
19
积分
18
注册时间
2022-12-27
最后登录
2022-12-27

发表于 2025-2-16 10:25:38 | 显示全部楼层
感谢楼主,顶。
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~

  离线 

TA的专栏

  • 打卡等级:即来则安
  • 打卡总天数:21
  • 打卡月天数:0
  • 打卡总奖励:259
  • 最近打卡:2025-03-14 16:28:27
等级头衔

等級:晓枫资讯-列兵

在线时间
0 小时

积分成就
威望
0
贡献
0
主题
0
精华
0
金钱
302
积分
50
注册时间
2023-2-16
最后登录
2025-3-14

发表于 5 天前 | 显示全部楼层
顶顶更健康!!!
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~
严禁发布广告,淫秽、色情、赌博、暴力、凶杀、恐怖、间谍及其他违反国家法律法规的内容。!晓枫资讯-社区
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

1楼
2楼
3楼

手机版|晓枫资讯--科技资讯社区 本站已运行

CopyRight © 2022-2025 晓枫资讯--科技资讯社区 ( BBS.yzwlo.com ) . All Rights Reserved .

晓枫资讯--科技资讯社区

本站内容由用户自主分享和转载自互联网,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。

如有侵权、违反国家法律政策行为,请联系我们,我们会第一时间及时清除和处理! 举报反馈邮箱:点击这里给我发消息

Powered by Discuz! X3.5

快速回复 返回顶部 返回列表