.NET Core、EF、Dapper、MySQL 多种方式实现数据库操作(动态注册实体类)
前言
最近在学习、研究 .NET Core 方面的知识,动手搭建了一些小的 Demo,对 .NET Core 有了初步的认识了解。
恰逢公司的项目需要,有一个需求,不大不小可以作为转 .NET Core 的示例项目来做。
这个项目的搭建工作由我来做,我这边做了一些技术预研,对呀用到的技术进行预研,其中包括:Kafka、SignalR、Topshelf。
但是在做单数据库的时候出现了一些问题。
一、技术选型
这里说的技术选型是数据操作这一块的。
我们之所以选择了 EF、Dapper 结合,是有原因的。以前是直接用的 EF DBFirst 直接拖库过来,用 Linq 语句查询,但是这样对于多张表的连表查询存在很大的隐患,因为这样生成的 SQL 语句不可靠,有时候生成的太复杂,效率太低,所以最终选择了 EF 和 Dapper 结合。对于简单的查询直接用 EF 操作,对于复杂一些的查询写 SQL 语句用 Dapper 查询。
这个选型是原先 .NET 下的,这次转的话,也按照这个来。
二、遇到的坑
在具体的实施中遇到了几个坑,下面就展开说说。
2.1、.NET Core 下 EF 的问题
.NET Core 下的 EF 和原先平台的有挺大的差别,首先构造函数的差别:
.NET 下的
public DbContext(string nameOrConnectionString); public DbContext(string nameOrConnectionString, DbCompiledModel model); public DbContext(DbConnection existingConnection, bool contextOwnsConnection); public DbContext(ObjectContext objectContext, bool dbContextOwnsObjectContext); public DbContext(DbConnection existingConnection, DbCompiledModel model, bool contextOwnsConnection); protected DbContext(); protected DbContext(DbCompiledModel model);
.NET Core 下的
public DbContext([NotNullAttribute] DbContextOptions options); protected DbContext();
主要是因为 .NET Core 下功能模块的注册使用的新的方式。
那么在.NET Core 下继承 DbContext 并对其扩展如下:
public class DbContextTest : DbContext { public DbContextTest(DbContextOptions<DbContextTest> options) : base(options) { } }
public class MySQLDatabase { #region 构造函数 /// <summary> /// 构造方法 /// </summary> /// <param name="connString">连接串</param> public MySQlDatabase(string connString) { var optionBuilder = new DbContextOptionsBuilder<DatabaseContext>();
optionBuilder.UseMySql(connString, mysqlOptions =>
{
mysqlOptions.ServerVersion(new Version(5, 7, 22), Pomelo.EntityFrameworkCore.MySql.Infrastructure.ServerType.MySql);
});
dbcontext = new DatabaseContext(optionBuilder.Options); Dapper.DefaultTypeMap.MatchNamesWithUnderscores = true; }
// 这里是扩展的一些方法 }
2.2、.net core >=2.1 数据库实体类的注册
在 .net core 2.2 后新增了 ApplyConfigurationsFromAssembly,配合 IEntityTypeConfiguration,可以很方便就注册实体类映射关系。
因为选择了 EF 和 Dapper 那么需要自己动态建实体类并注册。
原先写了一个实体类生成的工具,这里拿来直接用了,在注册的时候 EF 和以前的注册方式有了改变,这里有对其扩展的代码:
namespace DataBase.Mapping { public interface IEntityMappingConfiguration { void Map(ModelBuilder b); } public interface IEntityMappingConfiguration<T> : IEntityMappingConfiguration where T : class { void Map(EntityTypeBuilder<T> builder); } public abstract class EntityMappingConfiguration<T> : IEntityMappingConfiguration<T> where T : class { public abstract void Map(EntityTypeBuilder<T> b); public void Map(ModelBuilder b) { Map(b.Entity<T>()); } } public static class ModelBuilderExtenions { private static IEnumerable<Type> GetMappingTypes(this Assembly assembly, Type mappingInterface) { return assembly.GetTypes().Where(x => !x.IsAbstract && x.GetInterfaces().Any(y => y.GetTypeInfo().IsGenericType && y.GetGenericTypeDefinition() == mappingInterface)); } public static void AddEntityConfigurationsFromAssembly(this ModelBuilder modelBuilder, Assembly assembly) { var mappingTypes = assembly.GetMappingTypes(typeof(IEntityMappingConfiguration<>)); IEnumerable<IEntityMappingConfiguration> configs = mappingTypes.Select(Activator.CreateInstance).Cast<IEntityMappingConfiguration>(); foreach (var config in configs) { config.Map(modelBuilder); } } } }
实体类的 Mapping,这个是继承了上面的接口 :
public class ApplicationMap : EntityMappingConfiguration<ApplicationEntity> { public override void Map(EntityTypeBuilder<ApplicationEntity> b) { b.ToTable("application") .HasKey(p => p.Id); } }
那么在 DbContextTest 里面重写 OnModelCreating 方法:
protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); string assembleFileName = Assembly.GetExecutingAssembly().CodeBase.Replace("DataBase.dll", "Mapping.dll").Replace("file:///", ""); Assembly asm = Assembly.LoadFile(assembleFileName);
// .net core 2.1 前 modelBuilder.AddEntityConfigurationsFromAssembly(asm);
// .net core 2.2 后
modelBuilder.ApplyConfigurationsFromAssembly(asm);
}
这样代码工作基本完成了。
切记坑:
开始用的是 MySQL 官方的驱动:MySql.Data.EntityFrameworkCore ,但是一直报错:" The 'MySQLNumberTypeMapping' does not support value conversions. "
后来改用 Pomelo.EntityFrameworkCore.MySql 就可以了。
这个坑困扰了几天,真是憔悴了些。