.NetCore如何使用EFCore
创始人
2024-03-27 20:34:56
0

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
{public TKey Id { get; set; }
}

统一的接口实现类 

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

用户实体类

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

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

public class EFEntityInfo
{public (Assembly Assembly, IEnumerable Types) EFEntitiesInfo => (GetType().Assembly, GetEntityTypes(GetType().Assembly));private IEnumerable 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()获取用户的DBSet。

IEntityTypeConfiguration(表配置)

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

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

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

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

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

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

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

public class UserConfig : EntityTypeConfiguration
{public override void Configure(EntityTypeBuilder 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
{public override void Configure(EntityTypeBuilder 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(false);//导航属性,本质就是创建外键,虽然查询很方便,生产中不建议使用!!!}
}

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(仓储)

这个不过分介绍,特别是基于http的微服务中基本都有这个。
1.创建一个仓储基类,对于不同的实体,创建一样的增删改查方法。
简单写几个查询的方法定义。

public interface IAsyncRepository where TEntity : class
{IQueryable All();IQueryable All(string[] propertiesToInclude);IQueryable Where(Expression> filter);IQueryable Where(Expression> filter, string[] propertiesToInclude);
}

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

public class GenericRepository : IAsyncRepository where TEntity : class
{protected readonly LibraryDbContext _dbContext;public GenericRepository(LibraryDbContext dbContext){_dbContext = dbContext;}~GenericRepository(){_dbContext?.Dispose();}public virtual IQueryable All(){return All(null);}public virtual IQueryable All(string[] propertiesToInclude){var query = _dbContext.Set().AsNoTracking();if (propertiesToInclude != null){foreach (var property in propertiesToInclude.Where(p => !string.IsNullOrWhiteSpace(p))){query = query.Include(property);}}return query;}
}

Autofac

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

public class EFCoreEleganceUseEFCoreModule : Module
{protected override void Load(ContainerBuilder builder){base.Load(builder);builder.RegisterModule(); //注入domain模块builder.RegisterGeneric(typeof(GenericRepository<,>))//将dbcontext注入到仓储的构造中.UsingConstructor(typeof(LibraryDbContext)).AsImplementedInterfaces().InstancePerDependency();builder.RegisterType().As().InstancePerDependency();}
}

2.Domain注入EFEntityInfo

public class EFCoreEleganceUseDomainModule : Module
{protected override void Load(ContainerBuilder builder){builder.RegisterType().SingleInstance();}
}

数据库配置

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

var mysqlConfig = hostContext.Configuration.GetSection("Mysql").Get();
var serverVersion = new MariaDbServerVersion(new Version(mysqlConfig.Version));
services.AddDbContext(options =>
{options.UseMySql(mysqlConfig.ConnectionString, serverVersion, optionsBuilder =>{optionsBuilder.MinBatchSize(4);optionsBuilder.CommandTimeout(10);optionsBuilder.MigrationsAssembly(mysqlConfig.MigrationAssembly);//迁移文件所在的程序集optionsBuilder.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery);}).UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);//开发环境可以打开日志记录和显示详细的错误if (hostContext.HostingEnvironment.IsDevelopment()){options.EnableSensitiveDataLogging();options.EnableDetailedErrors();}
});

如需源码可以留言

相关内容

热门资讯

原创 在... 在古代,被皇帝赐死其实是一种特殊的待遇,而不仅仅是死亡本身值得关注,更应看赐字所体现的意义。 首...
原创 2... 近日,拥有287万粉丝的抖音网红主播王某强(账号名:某某超市)因被曝多次犯下刑事罪行引发舆论哗然,相...
全球货币政策为何出现明显分化? 2025年岁末,全球金融市场出现“超级央行周”。 从12月10日开始,美国、日本、英国、欧盟、俄罗斯...
广汽自主品牌“三担责”政策发布 IT之家 12 月 28 日消息,广汽集团今日正式发布自主品牌“三担责”政策:三电问题自燃、电池衰减...
原创 他... John McAvoy 是一位来自英国的铁人三项运动员,创造了多个世界纪录,其中包括100,000米...
原创 高... 12月26日这一天,对日本右翼势力来说,具有非常特殊的意义,甚至可能成为东亚局势的一个重要导火索。日...
石景山检察院:侵犯知识产权犯罪... 新京报讯(记者张静姝)12月24日上午,北京市石景山区人民检察院(以下简称“石景山区检察院”)在中关...
暖心!女子买鸭起纠纷,拍摄者自... 智慧中国讯(马泽川)2025年12月26日,湖南衡阳一市场内上演了暖心一幕。一名女子在老人摊位购买鸭...
原创 民... 如今,律师这个职业在社会上的口碑并不特别好,主要原因是有些律师缺乏正义感。在律师圈里,有一些知名律师...
为促进民用航空事业高质量发展提... 法治日报全媒体记者 蒲晓磊 2025年12月27日,十四届全国人大常委会第十九次会议表决通过新修订的...