目錄
前言
最近在學習、研究 .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、數據庫實體類的注冊
因為選擇了 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); modelBuilder.AddEntityConfigurationsFromAssembly(asm); }
這樣代碼工作基本完成了。
切記坑:
開始用的是 MySQL 官方的驅動:MySql.Data.EntityFrameworkCore ,但是一直報錯:" The 'MySQLNumberTypeMapping' does not support value conversions. "
后來改用 Pomelo.EntityFrameworkCore.MySql 就可以了。
這個坑困擾了幾天,真是憔悴了些。