├── Demo ├── Blogging.db ├── Blogging_1.db ├── Models │ ├── Blog.cs │ ├── Post.cs │ └── BloggingContext.cs ├── Demo.csproj └── Program.cs ├── DataAccessHelper ├── DataAccessHelper.csproj ├── IDbAccessor.cs ├── ITableMappable.cs ├── DynamicModelCacheKeyFactory.cs ├── TableMappingRule.cs ├── TableAccessMapping.cs ├── ExtendDbContext.cs ├── IMappingMutable.cs ├── MyExtensions.cs ├── IEFAvailable.cs ├── BaseDataAccessor.cs └── DataAccessor.cs ├── DataAccessHelper.Extension.Dapper ├── DataAccessHelper.Extensions.Dapper.csproj └── DapperExtensions.cs ├── LICENSE ├── DataAccessHelper.sln └── README.md /Demo/Blogging.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinRunhao/DataAccessHelper/HEAD/Demo/Blogging.db -------------------------------------------------------------------------------- /Demo/Blogging_1.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YinRunhao/DataAccessHelper/HEAD/Demo/Blogging_1.db -------------------------------------------------------------------------------- /DataAccessHelper/DataAccessHelper.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 8.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /DataAccessHelper.Extension.Dapper/DataAccessHelper.Extensions.Dapper.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /DataAccessHelper/IDbAccessor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace DataAccessHelper 7 | { 8 | /// 9 | /// EFCore SQL数据库接入层接口 10 | /// 11 | public interface IDbAccessor: IEFAvailable, IMappingMutable, IDisposable 12 | { 13 | /// 14 | /// 获取EFCore的DbContext 15 | /// 16 | /// DbContext 17 | DbContext GetDbContext(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Demo/Models/Blog.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Demo.Models 5 | { 6 | public partial class Blog 7 | { 8 | public Blog() 9 | { 10 | Posts = new HashSet(); 11 | } 12 | 13 | public long BlogId { get; set; } 14 | public string Url { get; set; } 15 | public long Rating { get; set; } 16 | 17 | public ICollection Posts { get; set; } 18 | 19 | public override string ToString() 20 | { 21 | return $"{{ID: {BlogId}, URL: {Url}, Rating: {Rating}}}"; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /DataAccessHelper/ITableMappable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace DataAccessHelper 6 | { 7 | /// 8 | /// 数据表映射规则接口 9 | /// 10 | public interface ITableMappable 11 | { 12 | /// 13 | /// 获取映射表名 14 | /// 15 | /// 条件类型 16 | /// 分表的映射类型 17 | /// 映射条件 18 | /// 19 | string GetMappingTableName(Type modelType, object condition); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Demo/Models/Post.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Demo.Models 5 | { 6 | public partial class Post 7 | { 8 | public long PostId { get; set; } 9 | public string Title { get; set; } 10 | public string Content { get; set; } 11 | public long BlogId { get; set; } 12 | public DateTime PostDate { get; set; } 13 | 14 | public Blog Blog { get; set; } 15 | 16 | public override string ToString() 17 | { 18 | return $"ID: {PostId} Title: {Title} PostDate: {PostDate.ToShortDateString()}"; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DataAccessHelper/DynamicModelCacheKeyFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Infrastructure; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | using System.Threading; 7 | 8 | namespace DataAccessHelper 9 | { 10 | public class DynamicModelCacheKeyFactory : IModelCacheKeyFactory 11 | { 12 | private static int m_Marker = 0; 13 | 14 | public static void ChangeTableMapping() 15 | { 16 | Interlocked.Increment(ref m_Marker); 17 | } 18 | 19 | public object Create(DbContext context) 20 | { 21 | return (context.GetType(), m_Marker); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /DataAccessHelper/TableMappingRule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace DataAccessHelper 6 | { 7 | /// 8 | /// 数据表映射规则结构体 9 | /// 10 | public struct TableMappingRule 11 | { 12 | /// 13 | /// 需要重新映射的类 14 | /// 15 | public Type MappingType 16 | { 17 | get; set; 18 | } 19 | 20 | /// 21 | /// 映射功能提供者 22 | /// 23 | public ITableMappable Mapper 24 | { 25 | get; set; 26 | } 27 | 28 | /// 29 | /// 映射条件(供映射功能提供者生成表名) 30 | /// 31 | public object Condition 32 | { 33 | get; set; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Demo/Demo.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp3.1 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | Always 20 | 21 | 22 | Always 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /DataAccessHelper/TableAccessMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace DataAccessHelper 6 | { 7 | /// 8 | /// 数据表映射结构体 9 | /// 10 | public struct TableAccessMapping 11 | { 12 | /// 13 | /// 实体类 14 | /// 15 | public Type MappingType 16 | { 17 | get; private set; 18 | } 19 | 20 | /// 21 | /// 数据表名 22 | /// 23 | public string TableName 24 | { 25 | get; private set; 26 | } 27 | 28 | public TableAccessMapping(Type mappingType, string tableName) 29 | { 30 | this.MappingType = mappingType; 31 | this.TableName = tableName; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /DataAccessHelper/ExtendDbContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Infrastructure; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | 7 | namespace DataAccessHelper 8 | { 9 | /// 10 | /// 对EFCore中DbContext的一个小扩展用于实现表类型和表名的动态映射,请重写Configuring方法而不是OnConfiguring;重写ModelCreating方法而不是OnModelCreating方法 11 | /// 12 | public abstract class ExtendDbContext : DbContext 13 | { 14 | protected ICollection m_TableMappingRule; 15 | 16 | public ExtendDbContext() 17 | { 18 | } 19 | 20 | public ExtendDbContext(ICollection rules) 21 | { 22 | this.m_TableMappingRule = rules; 23 | } 24 | 25 | protected sealed override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 26 | { 27 | if (!optionsBuilder.IsConfigured) 28 | { 29 | Configuring(optionsBuilder); 30 | optionsBuilder.ReplaceService(); 31 | } 32 | } 33 | 34 | protected sealed override void OnModelCreating(ModelBuilder modelBuilder) 35 | { 36 | ModelCreating(modelBuilder); 37 | modelBuilder.ChangeTableMapping(m_TableMappingRule); 38 | } 39 | 40 | protected abstract void Configuring(DbContextOptionsBuilder optionsBuilder); 41 | 42 | protected abstract void ModelCreating(ModelBuilder modelBuilder); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /DataAccessHelper/IMappingMutable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace DataAccessHelper 6 | { 7 | /// 8 | /// 数据库可变映射 9 | /// 10 | public interface IMappingMutable 11 | { 12 | /// 13 | /// 更换数据库 14 | /// 15 | /// 新的数据库连接字符串 16 | void ChangeDataBase(string connString); 17 | 18 | /// 19 | /// 更换数据库, 数据表映射使用传入的映射规则 20 | /// 21 | /// 新的数据库连接字符串 22 | /// 映射规则 23 | /// type类型不支持 24 | void ChangeDataBase(string connStr, List rules); 25 | 26 | /// 27 | /// 根据条件改变多个传入类型的映射数据表(此操作会导致当前操作的context释放掉,调用前需确保context的内容已保存) 28 | /// 29 | /// 映射规则 30 | /// type类型不支持 31 | /// 改变后的数据表映射 32 | List ChangeMappingTables(List rules); 33 | 34 | /// 35 | /// 根据条件改变传入类型的映射数据表(此操作会导致当前操作的context释放掉,调用前需确保context的内容已保存) 36 | /// 37 | /// 条件类型 38 | /// 要改变映射的实体类型 39 | /// 改变条件 40 | /// type类型不支持 41 | /// 改变后的数据表映射 42 | TableAccessMapping ChangeMappingTable(Type type, ITableMappable mapper, object condition); 43 | 44 | /// 45 | /// 获取所有实体类的数据表映射结构体 46 | /// 47 | /// 映射关系集合 48 | List GetTableNames(); 49 | 50 | /// 51 | /// 获取指定实体类的数据表映射结构体 52 | /// 53 | /// 实体类性 54 | /// 传入实体类的映射关系 55 | TableAccessMapping GetTableName(Type mappingType); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /DataAccessHelper.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30804.86 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DataAccessHelper", "DataAccessHelper\DataAccessHelper.csproj", "{8DE81211-00A1-4328-B491-7A3D20ED5D68}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Demo", "Demo\Demo.csproj", "{D5083E72-E3AC-40F7-A845-71862ECF225F}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataAccessHelper.Extensions.Dapper", "DataAccessHelper.Extension.Dapper\DataAccessHelper.Extensions.Dapper.csproj", "{BA12FA5D-6914-4C2A-85D7-F03BE73C50D2}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {8DE81211-00A1-4328-B491-7A3D20ED5D68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {8DE81211-00A1-4328-B491-7A3D20ED5D68}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {8DE81211-00A1-4328-B491-7A3D20ED5D68}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {8DE81211-00A1-4328-B491-7A3D20ED5D68}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {D5083E72-E3AC-40F7-A845-71862ECF225F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {D5083E72-E3AC-40F7-A845-71862ECF225F}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {D5083E72-E3AC-40F7-A845-71862ECF225F}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {D5083E72-E3AC-40F7-A845-71862ECF225F}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {BA12FA5D-6914-4C2A-85D7-F03BE73C50D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {BA12FA5D-6914-4C2A-85D7-F03BE73C50D2}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {BA12FA5D-6914-4C2A-85D7-F03BE73C50D2}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {BA12FA5D-6914-4C2A-85D7-F03BE73C50D2}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {2C659F3D-C81D-4063-9A97-E87EA9B6A22B} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /DataAccessHelper/MyExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Infrastructure; 3 | using Microsoft.EntityFrameworkCore.Storage; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | using System.Data.Common; 8 | using System.Threading.Tasks; 9 | 10 | namespace DataAccessHelper 11 | { 12 | public static class MyExtensions 13 | { 14 | /// 15 | /// 根据传入规则改变数据表的映射 16 | /// 17 | /// 18 | /// 映射规则 19 | public static void ChangeTableMapping(this ModelBuilder builder, ICollection mapping) 20 | { 21 | if (mapping != null) 22 | { 23 | string tableNm; 24 | foreach (var rule in mapping) 25 | { 26 | tableNm = rule.Mapper.GetMappingTableName(rule.MappingType, rule.Condition); 27 | builder.Entity(rule.MappingType).ToTable(tableNm); 28 | } 29 | } 30 | } 31 | 32 | /// 33 | /// 根据当前设置的映射规则获取数据库的建表语句 34 | /// 35 | /// DataAccessor 36 | /// 建表语句 37 | public static string GetTableCreateScript(this IDbAccessor accessor) 38 | { 39 | var context = accessor.GetDbContext(); 40 | var dbCreator = context.GetService(); 41 | return dbCreator.GenerateCreateScript(); 42 | } 43 | 44 | /// 45 | /// 执行SQL语句,参数用法可参考ExecuteSqlRawAsync方法 46 | /// 47 | /// 例:"update tbUser set Name='HaHa' where Id={0}" 48 | /// 49 | /// SQL语句 50 | /// 参数 51 | /// 受影响行数 52 | public static Task ExecuteSqlRawAsync(this IDbAccessor accessor, string sql, params object[] parameters) 53 | { 54 | var context = accessor.GetDbContext(); 55 | return context.Database.ExecuteSqlRawAsync(sql, parameters); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /DataAccessHelper.Extension.Dapper/DapperExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Infrastructure; 3 | using Microsoft.EntityFrameworkCore.Storage; 4 | using System; 5 | using System.Collections.Generic; 6 | using Dapper; 7 | using System.Data.Common; 8 | using System.Threading.Tasks; 9 | 10 | namespace DataAccessHelper.Extensions.Dapper 11 | { 12 | /// 13 | /// 使用Dapper实现原生SQL查询 14 | /// 15 | public static class DapperExtensions 16 | { 17 | /// 18 | /// 使用Dapper执行原生SQL查询,返回单个结果集 19 | /// 20 | /// 返回类型 21 | /// 22 | /// SQL语句 23 | /// 参数 24 | /// 25 | public static async Task> SqlQueryAsync(this IDbAccessor accessor, string sql, object param = null) 26 | { 27 | var context = accessor.GetDbContext(); 28 | DbConnection conn = context.Database.GetDbConnection(); 29 | DbTransaction tran = default; 30 | var efTran = context.Database.CurrentTransaction; 31 | if (efTran != null) 32 | { 33 | tran = efTran.GetDbTransaction(); 34 | } 35 | 36 | var ret = await conn.QueryAsync(sql, param, tran); 37 | return ret; 38 | } 39 | 40 | /// 41 | /// 使用Dapper执行原生SQL查询,返回多个结果集 42 | /// 43 | /// 44 | /// SQL语句 45 | /// 参数 46 | /// 47 | public static async Task SqlQueryMultipleAsync(this IDbAccessor accessor, string sql, object param = null) 48 | { 49 | var context = accessor.GetDbContext(); 50 | DbConnection conn = context.Database.GetDbConnection(); 51 | DbTransaction tran = default; 52 | var efTran = context.Database.CurrentTransaction; 53 | if (efTran != null) 54 | { 55 | tran = efTran.GetDbTransaction(); 56 | } 57 | 58 | return await conn.QueryMultipleAsync(sql, param, tran); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Demo/Models/BloggingContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using DataAccessHelper; 3 | using System.Collections.Generic; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Infrastructure; 6 | using Microsoft.EntityFrameworkCore.Metadata; 7 | 8 | namespace Demo.Models 9 | { 10 | // step1:派生自ExtendDbContext 11 | public partial class BloggingContext : ExtendDbContext 12 | { 13 | public virtual DbSet Blog { get; set; } 14 | public virtual DbSet Post { get; set; } 15 | 16 | // step2:实现基类的构造方法,但可以什么都不干 17 | public BloggingContext() 18 | { 19 | } 20 | 21 | // step2:实现基类的构造方法,但可以什么都不干 22 | public BloggingContext(ICollection rules):base(rules) 23 | { 24 | } 25 | 26 | private static string ConnectString 27 | { 28 | get; set; 29 | } 30 | 31 | public static void SetConnectString(string conStr) 32 | { 33 | ConnectString = conStr; 34 | } 35 | 36 | // step3:把重写OnConfiguring的代码移到Configuring方法中进行重写 37 | protected override void Configuring(DbContextOptionsBuilder optionsBuilder) 38 | { 39 | if (!optionsBuilder.IsConfigured) 40 | { 41 | optionsBuilder.UseSqlite(ConnectString) 42 | .UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking); 43 | } 44 | } 45 | 46 | // step4:把重写OnModelCreating的代码移到ModelCreating方法中进行重写 47 | protected override void ModelCreating(ModelBuilder modelBuilder) 48 | { 49 | modelBuilder.Entity(entity => 50 | { 51 | entity.HasKey(e => e.BlogId); 52 | 53 | entity.ToTable("Blog"); 54 | 55 | entity.Property(e => e.BlogId).ValueGeneratedNever(); 56 | 57 | entity.Property(e => e.Url).HasColumnType("VARCHAR (1024)"); 58 | }); 59 | 60 | modelBuilder.Entity(entity => 61 | { 62 | entity.HasKey(e => e.PostId); 63 | 64 | entity.ToTable("Post"); 65 | 66 | entity.Property(e => e.PostId).ValueGeneratedNever(); 67 | 68 | entity.Property(e => e.Content).HasColumnType("VARCHAR (1024)"); 69 | 70 | entity.Property(e => e.PostDate) 71 | .IsRequired() 72 | .HasColumnType("DATETIME"); 73 | 74 | entity.Property(e => e.Title).HasColumnType("VARCHAR (512)"); 75 | 76 | entity.HasOne(e => e.Blog) 77 | .WithMany(b => b.Posts) 78 | .HasForeignKey(e => e.BlogId); 79 | }); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DataAccessHelper 2 | 一个基于EFCore的工具类,对EFCore中的context操作做了进一步的封装,且支持一个实体类映射多个数据表。 3 | ## 使用方法 4 | 如Demo中的例子所示,示例程序中一个Post实体类对应有多个Post数据表,多个数据表按年月存放不同的数据。数据表的命名规则为PostyyyyMM(如Post201910)。 5 | ### 1.新建数据表命名规则提供类,实现ITableMappable接口 6 | ``` 7 | // 数据表命名规则提供类,用于根据不同的条件映射不同的数据表 8 | public class PostMapper : ITableMappable 9 | { 10 | // 根据条件返回对应的数据表名 11 | public string GetMappingTableName(Type modelType, object condition) 12 | { 13 | string ret = ""; 14 | 15 | if (condition is DateTime date) 16 | { 17 | if (modelType == typeof(Post)) 18 | { 19 | // Format like Post201906 20 | ret = $"Post{date.ToString("yyyyMM")}"; 21 | } 22 | } 23 | 24 | return ret; 25 | } 26 | } 27 | ``` 28 | ### 2.你的DbContext类需要改动一些代码 29 | 请将你代码中的派生自*DbContext*类改为派生自*ExtendDbContext*,*ExtendDbContext*几乎不会改变任何DbContext的行为。 30 | 31 | 1. 将你代码中的派生自*DbContext*类改为派生自*ExtendDbContext* 32 | 2. 实现基类的构造方法,但可以什么都不干 33 | 3. 把重写OnConfiguring的代码移到Configuring方法中进行重写 34 | 4. 把重写OnModelCreating的代码移到ModelCreating方法中进行重写 35 | 36 | ``` 37 | // step1:派生自ExtendDbContext 38 | public partial class BloggingContext : ExtendDbContext 39 | { 40 | public virtual DbSet Blog { get; set; } 41 | public virtual DbSet Post { get; set; } 42 | 43 | // step2:实现基类的构造方法,但可以什么都不干 44 | public BloggingContext() 45 | { 46 | } 47 | 48 | // step2:实现基类的构造方法,但可以什么都不干 49 | public BloggingContext(ICollection rules):base(rules) 50 | { 51 | } 52 | 53 | private static string ConnectString 54 | { 55 | get; set; 56 | } 57 | 58 | public static void SetConnectString(string conStr) 59 | { 60 | ConnectString = conStr; 61 | } 62 | 63 | // step3:把重写OnConfiguring的代码移到Configuring方法中进行重写 64 | protected override void Configuring(DbContextOptionsBuilder optionsBuilder) 65 | { 66 | if (!optionsBuilder.IsConfigured) 67 | { 68 | optionsBuilder.UseSqlite(ConnectString) 69 | .UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking); 70 | } 71 | } 72 | 73 | // step4:把重写OnModelCreating的代码移到ModelCreating方法中进行重写 74 | protected override void ModelCreating(ModelBuilder modelBuilder) 75 | { 76 | modelBuilder.Entity(entity => 77 | { 78 | entity.HasKey(e => e.BlogId); 79 | 80 | entity.ToTable("Blog"); 81 | 82 | entity.Property(e => e.BlogId).ValueGeneratedNever(); 83 | 84 | entity.Property(e => e.Url).HasColumnType("VARCHAR (1024)"); 85 | }); 86 | 87 | modelBuilder.Entity(entity => 88 | { 89 | entity.HasKey(e => e.PostId); 90 | 91 | entity.ToTable("Post"); 92 | 93 | entity.Property(e => e.PostId).ValueGeneratedNever(); 94 | 95 | entity.Property(e => e.Content).HasColumnType("VARCHAR (1024)"); 96 | 97 | entity.Property(e => e.PostDate) 98 | .IsRequired() 99 | .HasColumnType("DATETIME"); 100 | 101 | entity.Property(e => e.Title).HasColumnType("VARCHAR (512)"); 102 | 103 | entity.HasOne(e => e.Blog) 104 | .WithMany(b => b.Posts) 105 | .HasForeignKey(e => e.BlogId); 106 | }); 107 | } 108 | } 109 | ``` 110 | ### 3.正式使用 111 | ``` 112 | // 使用示例 113 | static void TestChangeTable(DataAccessor dal, ITableMappable mapper) 114 | { 115 | // 数据表映射条件 116 | DateTime sept = DateTime.Parse("2019-09-05"); 117 | DateTime oct = DateTime.Parse("2019-10-05"); 118 | 119 | // 切换Post实体类的映射的数据表,切换条件为2019年10月,理论上应该切换为数据表 "Post201910" 120 | dal.ChangeMappingTable(typeof(Post), mapper, oct); 121 | // 查询该表下的所有数据 122 | List octData = dal.GetAll().ToList(); 123 | Console.WriteLine("Oct. data"); 124 | foreach (Post item in octData) 125 | { 126 | Console.WriteLine(item); 127 | } 128 | 129 | // 切换Post实体类的映射的数据表,切换条件为2019年9月,理论上应该切换为数据表 "Post201909" 130 | dal.ChangeMappingTable(typeof(Post), mapper, sept); 131 | // 查询该表下的所有数据 132 | List septData = dal.GetAll().ToList(); 133 | Console.WriteLine("Sept. data"); 134 | foreach (Post item in septData) 135 | { 136 | Console.WriteLine(item); 137 | } 138 | } 139 | ``` 140 | -------------------------------------------------------------------------------- /DataAccessHelper/IEFAvailable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using System.Collections.Generic; 4 | using System.Data.Common; 5 | using System.Data; 6 | using System.Threading.Tasks; 7 | using System.Linq; 8 | using Microsoft.EntityFrameworkCore.Storage; 9 | 10 | namespace DataAccessHelper 11 | { 12 | /// 13 | /// EF相关方法 14 | /// 15 | public interface IEFAvailable 16 | { 17 | /// 18 | /// 是否已关闭 19 | /// 20 | /// 21 | bool IsClose(); 22 | 23 | /// 24 | /// 关闭连接 25 | /// 26 | void Close(); 27 | 28 | /// 29 | /// 获取某个表的Queryable 30 | /// 31 | /// 32 | /// 33 | IQueryable GetQueryable() where T : class; 34 | 35 | /// 36 | /// 获取某个表的所有数据 37 | /// 38 | /// 39 | /// 40 | IQueryable GetAll() where T : class; 41 | 42 | /// 43 | /// 根据表达式进行查询返回结果 44 | /// 45 | /// 查询的类 46 | /// 查询表达式 47 | /// 48 | IQueryable GetMany(Expression> expression) where T : class; 49 | 50 | /// 51 | /// 根据主键值来查询某条记录,单主键的表可直接输入主键,多主键的表注意主键次序 52 | /// 53 | /// 实体类型 54 | /// 主键值 55 | /// 返回主键值为传入值的实体 56 | T GetByID(params object[] values) where T : class; 57 | 58 | /// 59 | /// 根据主键值来查询某条记录,单主键的表可直接输入主键,多主键的表注意主键次序 60 | /// 61 | /// 实体类型 62 | /// 主键值 63 | /// 返回主键值为传入值的实体 64 | Task GetByIDAsync(params object[] values) where T : class; 65 | 66 | /// 67 | /// 插入数据 68 | /// 69 | /// 70 | /// 71 | /// 72 | T Add(T model) where T : class; 73 | 74 | /// 75 | /// 插入数据,并保存 76 | /// 77 | /// 78 | /// 79 | /// 80 | Task AddAsync(T model) where T : class; 81 | 82 | /// 83 | /// 向上下文增加记录,但不保存,需要手动调用Save 84 | /// 85 | /// 86 | /// 87 | /// 88 | void AddRecord(T model) where T : class; 89 | 90 | /// 91 | /// 按主键标记实体删除 92 | /// 93 | /// 94 | /// 95 | /// 96 | void Delete(T model) where T : class; 97 | 98 | /// 99 | /// 更新操作 100 | /// 101 | /// 102 | /// 103 | /// 104 | void Update(T model) where T : class; 105 | 106 | /// 107 | /// 根据主键更新指定字段,使用时需要注意EFCore的仓储机制可能会使数据库和缓存仓的数据不一致。 108 | /// 109 | /// 映射类 110 | /// 更新的实体 111 | /// 要更新的属性, VisualStudio在这里有Bug, 不能智能显示类型属性, 但不影响使用 112 | /// 113 | void Update(T model, Expression> property) where T : class; 114 | 115 | /// 116 | /// 根据某个字段的值进行排序 117 | /// 118 | /// 排序后获得的集合的类型 119 | /// 排序字段的类型 120 | /// 字段表达式 如:basicInfo下根据caseID排序(s=>s.caseID) 121 | /// 是否升序 122 | /// 123 | IEnumerable Order(Func orderExpression, bool isASC = false) where T : class; 124 | 125 | /// 126 | /// 在数据库中进行分页查询 127 | /// 128 | /// 查询的类 129 | /// 每页条数 130 | /// 当前页 131 | /// 查询表达式 132 | /// 133 | List GetPageList(int pageSize, int pageIdx, Expression> expression, Func orderExpression) where T : class; 134 | 135 | /// 136 | /// 在数据库中进行分页查询 137 | /// 138 | /// 查询的类 139 | /// 页大小 140 | /// 页下标 141 | /// 查询表达式 142 | /// 排序表达式 143 | /// 分页查询结果 144 | Task> GetPageListAsync(int pageSize, int pageIdx, Expression> expression, Func orderExpression) where T : class; 145 | 146 | /// 147 | /// 获取条目数 148 | /// 149 | /// 查询的类 150 | /// 查询表达式 151 | /// 条目数 152 | int GetCount(Expression> expression) where T : class; 153 | 154 | /// 155 | /// 获取条目数 156 | /// 157 | /// 查询的类 158 | /// 查询表达式 159 | /// 条目数 160 | Task GetCountAsync(Expression> expression) where T : class; 161 | 162 | /// 163 | /// 执行存储过程,返回存储过程中返回的数据表 164 | /// 165 | /// 存储过程名 166 | /// 参数 167 | /// 返回的数据表集合 168 | List CallProcedure(string procName, params DbParameter[] parameters); 169 | 170 | /// 171 | /// 执行存储过程,返回存储过程中返回的数据表 172 | /// 173 | /// 存储过程名 174 | /// 参数 175 | /// 返回的数据表集合 176 | Task> CallProcedureAsync(string procName, params DbParameter[] parameters); 177 | 178 | /// 179 | /// 提交对数据进行的处理,如无处理返回-1 180 | /// 181 | /// 182 | int Save(); 183 | 184 | /// 185 | /// 提交对数据进行的处理,如无处理返回-1 186 | /// 187 | /// 188 | Task SaveAsync(); 189 | 190 | /// 191 | /// 异步获取一个事务对象 192 | /// 193 | /// 194 | Task BeginTransactionAsync(); 195 | 196 | /// 197 | /// 获取一个事务对象 198 | /// 199 | /// 200 | IDbContextTransaction BeginTransaction(); 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /Demo/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Collections.Generic; 5 | using Demo.Models; 6 | using DataAccessHelper; 7 | using DataAccessHelper.Extensions.Dapper; 8 | using System.Threading.Tasks; 9 | 10 | namespace Demo 11 | { 12 | class Program 13 | { 14 | static async Task Main(string[] args) 15 | { 16 | Init(); 17 | 18 | ITableMappable mapper = new PostMapper(); 19 | DataAccessor dal = DataAccessor.Create(); 20 | TestTableName(dal, mapper); 21 | 22 | TestChangeTable(dal, mapper); 23 | 24 | BasicUsage(dal); 25 | 26 | TestChangeDb(dal, mapper); 27 | 28 | TestGetTableSQL(dal, mapper); 29 | 30 | await TestSqlQueryAsync(dal); 31 | 32 | await TestSqlExecuteAsync(dal); 33 | dal.Close(); 34 | Console.WriteLine("Test Finish press any key to exit"); 35 | 36 | Console.ReadKey(); 37 | } 38 | 39 | static void Init() 40 | { 41 | string dbPath = Directory.GetCurrentDirectory(); 42 | dbPath = Path.Combine(dbPath, "Blogging.db"); 43 | string conStr = $"Data Source={dbPath}"; 44 | 45 | BloggingContext.SetConnectString(conStr); 46 | } 47 | 48 | static void TestTableName(DataAccessor dal, ITableMappable mapper) 49 | { 50 | var mapping = dal.GetTableName(typeof(Post)); 51 | Console.WriteLine($"Original table name: {mapping.TableName}"); 52 | 53 | dal.ChangeMappingTable(typeof(Post), mapper, DateTime.Parse("2019-09-05")); 54 | mapping = dal.GetTableName(typeof(Post)); 55 | Console.WriteLine($"Update table name: {mapping.TableName}\n"); 56 | } 57 | 58 | static void TestChangeTable(DataAccessor dal, ITableMappable mapper) 59 | { 60 | DateTime sept = DateTime.Parse("2019-09-05"); 61 | DateTime oct = DateTime.Parse("2019-10-05"); 62 | 63 | dal.ChangeMappingTable(typeof(Post), mapper, oct); 64 | List octData = dal.GetAll().ToList(); 65 | Console.WriteLine("Oct. data"); 66 | foreach (Post item in octData) 67 | { 68 | Console.WriteLine(item); 69 | } 70 | 71 | dal.ChangeMappingTable(typeof(Post), mapper, sept); 72 | List septData = dal.GetAll().ToList(); 73 | Console.WriteLine("Sept. data"); 74 | foreach (Post item in septData) 75 | { 76 | Console.WriteLine(item); 77 | } 78 | } 79 | 80 | static void BasicUsage(DataAccessor dal) 81 | { 82 | Console.WriteLine("\nTest BasicUsage"); 83 | long newId = 2; 84 | // Add 85 | Console.WriteLine("Test Add"); 86 | Console.WriteLine("Org data:"); 87 | var list = dal.GetAll().ToList(); 88 | PrintData(list); 89 | Blog newData = new Blog { BlogId = newId, Rating = 666, Url = "https://blog.test.com" }; 90 | dal.AddRecord(newData); 91 | dal.Save(); 92 | Console.WriteLine("New data:"); 93 | list = dal.GetAll().ToList(); 94 | PrintData(list); 95 | Console.WriteLine(); 96 | 97 | // update whole entity 98 | Blog target = dal.GetByID(newId); 99 | Console.WriteLine("Test update whole entity"); 100 | Console.WriteLine("Org data:"); 101 | Console.WriteLine(target); 102 | target.Url = "https://newurl.test.com"; 103 | target.Rating = 2610; 104 | dal.Update(target); 105 | dal.Save(); 106 | var item = dal.GetMany(s => s.BlogId == newId).FirstOrDefault(); 107 | Console.WriteLine("New data:"); 108 | Console.WriteLine(item); 109 | Console.WriteLine(); 110 | 111 | // update one property 112 | TestUpdateProperty(); 113 | 114 | // delete 115 | Console.WriteLine("Test Delete"); 116 | dal.Delete(target); 117 | dal.Save(); 118 | list = dal.GetAll().ToList(); 119 | PrintData(list); 120 | Console.WriteLine(); 121 | } 122 | 123 | static void TestChangeDb(DataAccessor dal, ITableMappable mapper) 124 | { 125 | Console.WriteLine("\nTest change Db"); 126 | string dbPath = Directory.GetCurrentDirectory(); 127 | dbPath = Path.Combine(dbPath, "Blogging_1.db"); 128 | string conStr = $"Data Source={dbPath}"; 129 | 130 | Console.WriteLine("Org Data:"); 131 | var list = dal.GetAll().ToList(); 132 | PrintData(list); 133 | 134 | dal.ChangeDataBase(conStr); 135 | 136 | Console.WriteLine("Changed Data:"); 137 | list = dal.GetAll().ToList(); 138 | PrintData(list); 139 | } 140 | 141 | static void TestGetTableSQL(DataAccessor dal, ITableMappable mapper) 142 | { 143 | Console.WriteLine("\nTest Get Db SQL"); 144 | Console.WriteLine("Original SQL"); 145 | var sql = dal.GetTableCreateScript(); 146 | Console.WriteLine(sql); 147 | dal.ChangeMappingTable(typeof(Post), mapper, DateTime.Now); 148 | Console.WriteLine("New SQL"); 149 | sql = dal.GetTableCreateScript(); 150 | Console.WriteLine(sql); 151 | } 152 | 153 | static async Task TestSqlQueryAsync(DataAccessor dal) 154 | { 155 | Console.WriteLine("\nTest Get SQL Query without transaction"); 156 | string sql = "select * from Blog;"; 157 | // 不带事务 158 | var data = await dal.SqlQueryAsync(sql); 159 | 160 | PrintData(data); 161 | // 带事务 162 | var tran = await dal.BeginTransactionAsync(); 163 | // 将自动填装上事务 164 | data = await dal.SqlQueryAsync(sql); 165 | Console.WriteLine("\nTest Get SQL Query with transaction"); 166 | PrintData(data); 167 | await tran.CommitAsync(); 168 | } 169 | 170 | static async Task TestSqlExecuteAsync(DataAccessor dal) 171 | { 172 | Console.WriteLine("\nTest Get SQL Execute"); 173 | long testId = 1; 174 | string sql = "update Blog set Rating=0 where BlogId={0}"; 175 | var tran = await dal.BeginTransactionAsync(); 176 | Console.WriteLine($"\nExecute SQL : {sql}"); 177 | int effect = await dal.ExecuteSqlRawAsync(sql, testId); 178 | Console.WriteLine($"\nEffect rows : {effect}"); 179 | await tran.RollbackAsync(); 180 | var record = await dal.GetByIDAsync(testId); 181 | Console.WriteLine($"After rollabck:{record}"); 182 | } 183 | 184 | static void PrintData(IEnumerable arr) 185 | { 186 | foreach (T item in arr) 187 | { 188 | Console.WriteLine(item); 189 | } 190 | } 191 | 192 | /// 193 | /// 测试更新单个字段 194 | /// 195 | private static void TestUpdateProperty() 196 | { 197 | var dal = DataAccessor.Create(); 198 | Console.WriteLine("Test update one property"); 199 | var data = dal.GetByID((long)1); 200 | Console.WriteLine("Org data:"); 201 | Console.WriteLine(data); 202 | data.Url = "https://jojo.test.com"; 203 | data.Rating = 2; 204 | // only update Url, the change of Rating will be ignored 205 | dal.Update(data, s => s.Url); 206 | dal.Save(); 207 | 208 | // 这里需要先关掉再重开,因为EFCore的仓储模式机制,data对象虽然在数据库只更新了Url属性,但它在EFCore的缓存仓里面是改了Url和Rating 209 | dal.Close(); 210 | dal = DataAccessor.Create(); 211 | var itemFromDb = dal.GetByID(data.BlogId); 212 | Console.WriteLine("New data:"); 213 | Console.WriteLine(itemFromDb); 214 | Console.WriteLine(); 215 | } 216 | } 217 | 218 | // step5: 编写你的表名映射规则 219 | public class PostMapper : ITableMappable 220 | { 221 | public string GetMappingTableName(Type modelType, object condition) 222 | { 223 | string ret = ""; 224 | 225 | if (condition is DateTime date) 226 | { 227 | if (modelType == typeof(Post)) 228 | { 229 | // Format like Post201906 230 | ret = $"Post{date.ToString("yyyyMM")}"; 231 | } 232 | } 233 | 234 | return ret; 235 | } 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /DataAccessHelper/BaseDataAccessor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using System.Collections.Generic; 4 | using Microsoft.EntityFrameworkCore; 5 | using System.Data.Common; 6 | using System.Data; 7 | using System.Threading.Tasks; 8 | using System.Linq; 9 | using Microsoft.EntityFrameworkCore.Storage; 10 | 11 | namespace DataAccessHelper 12 | { 13 | /// 14 | /// .NET EF Core框架帮助基础类 15 | /// 16 | internal class BaseDataAccessor : IEFAvailable 17 | { 18 | private ExtendDbContext context; 19 | 20 | public static BaseDataAccessor Create(Type tp, ICollection rules) 21 | { 22 | ExtendDbContext ctx = Activator.CreateInstance(tp, rules) as ExtendDbContext; 23 | BaseDataAccessor ret = new BaseDataAccessor(ctx); 24 | return ret; 25 | } 26 | 27 | public static BaseDataAccessor Create(Type tp) 28 | { 29 | ExtendDbContext ctx = Activator.CreateInstance(tp) as ExtendDbContext; 30 | BaseDataAccessor ret = new BaseDataAccessor(ctx); 31 | return ret; 32 | } 33 | 34 | /// 35 | /// 创建Helper对象 36 | /// 37 | /// EFCore DbContext 38 | private BaseDataAccessor(ExtendDbContext ctx) 39 | { 40 | context = ctx; 41 | } 42 | 43 | /// 44 | /// 是否已关闭 45 | /// 46 | /// 47 | public bool IsClose() 48 | { 49 | return context == null; 50 | } 51 | 52 | /// 53 | /// 关闭连接 54 | /// 55 | public void Close() 56 | { 57 | if (context != null) 58 | { 59 | context.Dispose(); 60 | context = null; 61 | } 62 | } 63 | 64 | /// 65 | /// 获取某个表的Queryable 66 | /// 67 | /// 68 | /// 69 | public IQueryable GetQueryable() where T : class 70 | { 71 | var query = context.Set().AsQueryable(); 72 | return query; 73 | } 74 | 75 | /// 76 | /// 获取某个表的所有数据 77 | /// 78 | /// 79 | /// 80 | public IQueryable GetAll() where T : class 81 | { 82 | var result = context.Set().Where(s => true); 83 | return result; 84 | } 85 | 86 | /// 87 | /// 根据表达式进行查询返回结果 88 | /// 89 | /// 查询的类 90 | /// 查询表达式 91 | /// 92 | public IQueryable GetMany(Expression> expression) where T : class 93 | { 94 | // var result = context.Set().AsExpandable().Where(expression); 95 | var result = context.Set().Where(expression); 96 | return result; 97 | } 98 | 99 | /// 100 | /// 根据主键值来查询某条记录,单主键的表可直接输入主键,多主键的表注意主键次序 101 | /// 102 | /// 实体类型 103 | /// 主键值 104 | /// 返回主键值为传入值的实体 105 | public T GetByID(params object[] values) where T : class 106 | { 107 | return context.Set().Find(values); 108 | } 109 | 110 | /// 111 | /// 根据主键值来查询某条记录,单主键的表可直接输入主键,多主键的表注意主键次序 112 | /// 113 | /// 实体类型 114 | /// 主键值 115 | /// 返回主键值为传入值的实体 116 | public async Task GetByIDAsync(params object[] values) where T : class 117 | { 118 | return await context.Set().FindAsync(values); 119 | } 120 | 121 | /// 122 | /// 插入数据 123 | /// 124 | /// 125 | /// 126 | /// 127 | public T Add(T model) where T : class 128 | { 129 | T retModel; 130 | if (model != null) 131 | { 132 | context.Set().Add(model); 133 | context.SaveChanges(); 134 | retModel = model; 135 | } 136 | else 137 | { 138 | retModel = null; 139 | } 140 | 141 | return retModel; 142 | } 143 | 144 | /// 145 | /// 插入数据 146 | /// 147 | /// 148 | /// 149 | /// 150 | public async Task AddAsync(T model) where T : class 151 | { 152 | T retModel; 153 | 154 | if (model != null) 155 | { 156 | context.Set().Add(model); 157 | await context.SaveChangesAsync(); 158 | retModel = model; 159 | } 160 | else 161 | { 162 | retModel = null; 163 | } 164 | 165 | return retModel; 166 | } 167 | 168 | /// 169 | /// 向上下文增加记录,但不保存,需要手动调用Commit 170 | /// 171 | /// 172 | /// 173 | /// 174 | public void AddRecord(T model) where T : class 175 | { 176 | if (model != null) 177 | { 178 | context.Set().Add(model); 179 | } 180 | } 181 | 182 | /// 183 | /// 按主键标记实体删除(即使传入的实体不是被追踪的实体,同主键的追踪实体依然会标记删除删除) 184 | /// 185 | /// 186 | /// 187 | /// 188 | public void Delete(T model) where T : class 189 | { 190 | try 191 | { 192 | var entity = context.Remove(model); 193 | } 194 | catch (InvalidOperationException e) 195 | { 196 | // 若在追踪列表中已经存在另一个实体和该实体有相同主键(但不是同一个) 197 | // 则找到追踪列表里的实体把它标记为删除 198 | Type modelType = typeof(T); 199 | var key = context.Model.FindEntityType(modelType).FindPrimaryKey(); 200 | if (key == null) 201 | { 202 | throw e; 203 | } 204 | else 205 | { 206 | // 找主键 207 | var props = key.Properties; 208 | object[] param = new object[props.Count]; 209 | int idx = 0; 210 | foreach (var p in props) 211 | { 212 | var clrProp = modelType.GetProperty(p.Name); 213 | var val = clrProp.GetValue(model); 214 | param[idx] = val; 215 | idx++; 216 | } 217 | // 用主键找实体,标记为删除 218 | var cacheModel = context.Set().Find(param); 219 | if (cacheModel != null) 220 | { 221 | context.Remove(cacheModel); 222 | } 223 | } 224 | } 225 | } 226 | 227 | /// 228 | /// 更新操作 229 | /// 230 | /// 231 | /// 232 | /// 233 | public void Update(T model) where T : class 234 | { 235 | var entity = context.Entry(model); 236 | entity.State = EntityState.Modified; 237 | } 238 | 239 | /// 240 | /// 根据主键更新指定字段,使用时需要注意EFCore的仓储机制可能会使数据库和缓存仓的数据不一致。 241 | /// 242 | /// 映射类 243 | /// 字段类型 244 | /// 更新的实体 245 | /// 要更新的属性, VisualStudio在这里有Bug, 不能智能显示类型属性, 但不影响使用 246 | /// 247 | public void Update(T model, Expression> property) where T : class 248 | { 249 | var entity = context.Entry(model); 250 | entity.Property(property).IsModified = true; 251 | } 252 | 253 | /// 254 | /// 根据某个字段的值进行排序 255 | /// 256 | /// 排序后获得的集合的类型 257 | /// 排序字段的类型 258 | /// 字段表达式 如:basicInfo下根据caseID排序(s=>s.caseID) 259 | /// 是否升序 260 | /// 261 | public IEnumerable Order(Func orderExpression, bool isASC = false) where T : class 262 | { 263 | if (isASC) 264 | return context.Set().OrderBy(orderExpression); 265 | else 266 | return context.Set().OrderByDescending(orderExpression); 267 | } 268 | 269 | /// 270 | /// 在数据库中进行分页查询 271 | /// 272 | /// 查询的类 273 | /// 每页条数 274 | /// 当前页 275 | /// 查询表达式 276 | /// 277 | public List GetPageList(int pageSize, int pageIdx, Expression> expression, Func orderExpression) where T : class 278 | { 279 | var datas = this.GetMany(expression); 280 | var result = datas.OrderBy(orderExpression).Skip(pageSize * (pageIdx - 1)).Take(pageSize); 281 | return result.ToList(); 282 | } 283 | 284 | /// 285 | /// 在数据库中进行分页查询 286 | /// 287 | /// 查询的类 288 | /// 页大小 289 | /// 页下标 290 | /// 查询表达式 291 | /// 排序表达式 292 | /// 分页查询结果 293 | public async Task> GetPageListAsync(int pageSize, int pageIdx, Expression> expression, Func orderExpression) where T : class 294 | { 295 | var datas = this.GetMany(expression); 296 | var result = datas.OrderBy(orderExpression).Skip(pageSize * (pageIdx - 1)).Take(pageSize).AsQueryable(); 297 | var ret = await result.ToListAsync(); 298 | return ret; 299 | } 300 | 301 | /// 302 | /// 获取条目数 303 | /// 304 | /// 查询的类 305 | /// 查询表达式 306 | /// 条目数 307 | public int GetCount(Expression> expression) where T : class 308 | { 309 | int ret = 0; 310 | var query = this.GetMany(expression); 311 | ret = query.Count(); 312 | return ret; 313 | } 314 | 315 | /// 316 | /// 获取条目数 317 | /// 318 | /// 查询的类 319 | /// 查询表达式 320 | /// 条目数 321 | public async Task GetCountAsync(Expression> expression) where T : class 322 | { 323 | var query = this.GetMany(expression); 324 | int ret = await query.CountAsync(); 325 | return ret; 326 | } 327 | 328 | /// 329 | /// 执行存储过程,返回存储过程中返回的数据表 330 | /// 331 | /// 存储过程名 332 | /// 参数 333 | /// 返回的数据表 334 | public List CallProcedure(string procName, params DbParameter[] parameters) 335 | { 336 | List ret = new List(); 337 | var connection = context.Database.GetDbConnection(); 338 | DbDataReader reader = null; 339 | DataTable table = null; 340 | using DbCommand cmd = connection.CreateCommand(); 341 | IDbContextTransaction tran = context.Database.CurrentTransaction; 342 | if (tran != null) 343 | { 344 | cmd.Transaction = tran.GetDbTransaction(); 345 | } 346 | 347 | cmd.CommandType = System.Data.CommandType.StoredProcedure; 348 | cmd.CommandText = procName; 349 | cmd.Parameters.AddRange(parameters); 350 | context.Database.OpenConnection(); 351 | reader = cmd.ExecuteReader(); 352 | table = ReaderToDataTable(reader); 353 | ret.Add(table); 354 | while (reader.NextResult()) 355 | { 356 | table = ReaderToDataTable(reader); 357 | ret.Add(table); 358 | } 359 | reader.Close(); 360 | //context.Database.CloseConnection(); 361 | 362 | return ret; 363 | } 364 | 365 | /// 366 | /// 执行存储过程,返回存储过程中返回的数据表 367 | /// 368 | /// 存储过程名 369 | /// 参数 370 | /// 返回的数据表 371 | public async Task> CallProcedureAsync(string procName, params DbParameter[] parameters) 372 | { 373 | List ret = new List(); 374 | var connection = context.Database.GetDbConnection(); 375 | DbDataReader reader = null; 376 | DataTable table = null; 377 | using DbCommand cmd = connection.CreateCommand(); 378 | IDbContextTransaction tran = context.Database.CurrentTransaction; 379 | if (tran != null) 380 | { 381 | cmd.Transaction = tran.GetDbTransaction(); 382 | } 383 | 384 | cmd.CommandType = System.Data.CommandType.StoredProcedure; 385 | cmd.CommandText = procName; 386 | cmd.Parameters.AddRange(parameters); 387 | await context.Database.OpenConnectionAsync(); 388 | reader = await cmd.ExecuteReaderAsync(); 389 | table = ReaderToDataTable(reader); 390 | ret.Add(table); 391 | while (reader.NextResult()) 392 | { 393 | table = ReaderToDataTable(reader); 394 | ret.Add(table); 395 | } 396 | reader.Close(); 397 | //context.Database.CloseConnection(); 398 | 399 | return ret; 400 | } 401 | 402 | /// 403 | /// 提交对数据进行的处理,如无处理返回-1 404 | /// 405 | /// 406 | public int Save() 407 | { 408 | if (context != null) 409 | return context.SaveChanges(); 410 | else 411 | return -1; 412 | } 413 | 414 | /// 415 | /// 提交对数据进行的处理,如无处理返回-1 416 | /// 417 | /// 418 | public async Task SaveAsync() 419 | { 420 | if (context != null) 421 | return await context.SaveChangesAsync(); 422 | else 423 | return -1; 424 | } 425 | 426 | /// 427 | /// 异步获取一个事务对象 428 | /// 429 | /// 430 | public async Task BeginTransactionAsync() 431 | { 432 | var ret = await context.Database.BeginTransactionAsync(); 433 | return ret; 434 | } 435 | 436 | /// 437 | /// 获取一个事务对象 438 | /// 439 | /// 440 | public IDbContextTransaction BeginTransaction() 441 | { 442 | return context.Database.BeginTransaction(); 443 | } 444 | 445 | public DbContext GetDbContext() 446 | { 447 | return context; 448 | } 449 | 450 | private DataTable ReaderToDataTable(DbDataReader reader) 451 | { 452 | DataTable table = new DataTable(); 453 | for (int i = 0; i < reader.FieldCount; i++) 454 | { 455 | DataColumn column = new DataColumn(); 456 | column.DataType = reader.GetFieldType(i); 457 | column.ColumnName = reader.GetName(i); 458 | table.Columns.Add(column); 459 | } 460 | 461 | while (reader.Read()) 462 | { 463 | DataRow row = table.NewRow(); 464 | for (int i = 0; i < reader.FieldCount; i++) 465 | { 466 | row[i] = reader[i]; 467 | } 468 | table.Rows.Add(row); 469 | } 470 | 471 | return table; 472 | } 473 | } 474 | } 475 | -------------------------------------------------------------------------------- /DataAccessHelper/DataAccessor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Data.Common; 5 | using System.Linq; 6 | using System.Linq.Expressions; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | using Microsoft.EntityFrameworkCore; 10 | using Microsoft.EntityFrameworkCore.Infrastructure; 11 | using Microsoft.EntityFrameworkCore.Metadata; 12 | using Microsoft.EntityFrameworkCore.Storage; 13 | 14 | namespace DataAccessHelper 15 | { 16 | /// 17 | /// .NET EF Core框架帮助类 18 | /// 19 | public class DataAccessor : IDbAccessor 20 | { 21 | /// 22 | /// 更改映射时的互斥锁 23 | /// 24 | private static object LockObj = new object(); 25 | 26 | private BaseDataAccessor m_BaseAccessor; 27 | 28 | private Type m_CtxTp; 29 | 30 | /// 31 | /// 使用指定的DbContext创建一个新的实例 32 | /// 33 | /// 指定的DbContext 34 | /// 新实例 35 | public static DataAccessor Create() where T : ExtendDbContext 36 | { 37 | var accessor = BaseDataAccessor.Create(typeof(T)); 38 | return new DataAccessor(accessor, typeof(T)); 39 | } 40 | 41 | private DataAccessor(BaseDataAccessor accessor, Type ctxTp) 42 | { 43 | m_BaseAccessor = accessor; 44 | m_CtxTp = ctxTp; 45 | } 46 | 47 | #region IMappingMutable接口实现 48 | /// 49 | /// 更换数据库, 数据表映射使用默认映射规则 50 | /// 51 | /// 新的数据库连接字符串 52 | public void ChangeDataBase(string connStr) 53 | { 54 | // close 55 | var accessor = m_BaseAccessor; 56 | if (!accessor.IsClose()) 57 | { 58 | accessor.Close(); 59 | } 60 | // new base accessor 61 | accessor = BaseDataAccessor.Create(m_CtxTp); 62 | accessor.GetDbContext().Database.GetDbConnection().ConnectionString = connStr; 63 | m_BaseAccessor = accessor; 64 | } 65 | 66 | /// 67 | /// 更换数据库, 数据表映射使用传入的映射规则 68 | /// 69 | /// 新的数据库连接字符串 70 | /// 映射规则 71 | /// type类型不支持 72 | public void ChangeDataBase(string connStr, List rules) 73 | { 74 | // close 75 | var accessor = m_BaseAccessor; 76 | if (!accessor.IsClose()) 77 | { 78 | accessor.Close(); 79 | } 80 | lock (LockObj) 81 | { 82 | // new base accessor 83 | accessor = BaseDataAccessor.Create(m_CtxTp, rules); 84 | // notity new mapping 85 | DynamicModelCacheKeyFactory.ChangeTableMapping(); 86 | accessor.GetDbContext().Database.GetDbConnection().ConnectionString = connStr; 87 | m_BaseAccessor = accessor; 88 | } 89 | } 90 | 91 | /// 92 | /// 根据条件改变多个传入类型的映射数据表(此操作会导致当前操作的context释放掉,调用前需确保context的内容已保存) 93 | /// 94 | /// 映射规则 95 | /// type类型不支持 96 | /// 改变后的数据表映射 97 | public List ChangeMappingTables(List rules) 98 | { 99 | List ret = new List(); 100 | if (rules != null) 101 | { 102 | // close old accessor 103 | var accessor = m_BaseAccessor; 104 | if (!accessor.IsClose()) 105 | { 106 | accessor.Close(); 107 | } 108 | lock (LockObj) 109 | { 110 | // new accessor 111 | accessor = BaseDataAccessor.Create(m_CtxTp, rules); 112 | // notity new mapping 113 | DynamicModelCacheKeyFactory.ChangeTableMapping(); 114 | this.m_BaseAccessor = accessor; 115 | } 116 | foreach (var rule in rules) 117 | { 118 | var mapping = GetTableName(rule.MappingType, accessor); 119 | ret.Add(mapping); 120 | } 121 | } 122 | 123 | return ret; 124 | } 125 | 126 | /// 127 | /// 根据条件改变传入类型的映射数据表(此操作会导致当前操作的context释放掉,调用前需确保context的内容已保存) 128 | /// 129 | /// 条件类型 130 | /// 要改变映射的实体类型 131 | /// 改变条件 132 | /// type类型不支持 133 | /// 改变后的数据表映射 134 | public TableAccessMapping ChangeMappingTable(Type type, ITableMappable mapper, object condition) 135 | { 136 | TableMappingRule rule = default(TableMappingRule); 137 | rule.MappingType = type; 138 | rule.Mapper = mapper; 139 | rule.Condition = condition; 140 | 141 | List param = new List { rule }; 142 | var result = ChangeMappingTables(param); 143 | return result[0]; 144 | } 145 | 146 | /// 147 | /// 获取所有实体类的数据表映射结构体 148 | /// 149 | /// 映射关系集合 150 | public List GetTableNames() 151 | { 152 | BaseDataAccessor helper = m_BaseAccessor; 153 | var context = helper.GetDbContext(); 154 | 155 | List ret = new List(); 156 | var models = context.Model.GetEntityTypes(); 157 | foreach (var model in models) 158 | { 159 | string table = model.GetTableName(); 160 | Type type = model.ClrType; 161 | TableAccessMapping mapping = new TableAccessMapping(type, table); 162 | ret.Add(mapping); 163 | } 164 | 165 | return ret; 166 | } 167 | 168 | /// 169 | /// 获取指定实体类的数据表映射结构体 170 | /// 171 | /// 实体类性 172 | /// 传入实体类的映射关系 173 | public TableAccessMapping GetTableName(Type mappingType) 174 | { 175 | BaseDataAccessor helper = m_BaseAccessor; 176 | var context = helper.GetDbContext(); 177 | var model = context.Model.FindEntityType(mappingType); 178 | 179 | if (model != null) 180 | { 181 | string table = model.GetTableName(); 182 | return new TableAccessMapping(mappingType, table); 183 | } 184 | else 185 | { 186 | throw new ArgumentException("Mapping type not found"); 187 | } 188 | } 189 | #endregion 190 | 191 | #region IDataAccessor接口实现 192 | /// 193 | /// 获取某个表的Queryable 194 | /// 195 | /// 196 | /// 197 | public IQueryable GetQueryable() where T : class 198 | { 199 | return m_BaseAccessor.GetQueryable(); 200 | } 201 | 202 | /// 203 | /// 插入数据 204 | /// 205 | /// 206 | /// 207 | /// 208 | public T Add(T model) where T : class 209 | { 210 | return m_BaseAccessor?.Add(model); 211 | } 212 | 213 | /// 214 | /// 插入数据并保存 215 | /// 216 | /// 217 | /// 218 | /// 219 | public Task AddAsync(T model) where T : class 220 | { 221 | return m_BaseAccessor?.AddAsync(model); 222 | } 223 | 224 | /// 225 | /// 向上下文增加记录,但不保存,需要手动调用Save 226 | /// 227 | /// 228 | /// 229 | /// 230 | public void AddRecord(T model) where T : class 231 | { 232 | m_BaseAccessor?.AddRecord(model); 233 | } 234 | 235 | /// 236 | /// 获取一个事务对象 237 | /// 238 | /// 239 | public IDbContextTransaction BeginTransaction() 240 | { 241 | return m_BaseAccessor?.BeginTransaction(); 242 | } 243 | 244 | /// 245 | /// 异步获取一个事务对象 246 | /// 247 | /// 248 | public Task BeginTransactionAsync() 249 | { 250 | return m_BaseAccessor?.BeginTransactionAsync(); 251 | } 252 | 253 | /// 254 | /// 执行存储过程,返回存储过程中返回的数据表 255 | /// 256 | /// 存储过程名 257 | /// 参数 258 | /// 返回的数据表 259 | public List CallProcedure(string procName, params DbParameter[] parameters) 260 | { 261 | return m_BaseAccessor?.CallProcedure(procName, parameters); 262 | } 263 | 264 | /// 265 | /// 执行存储过程,返回存储过程中返回的数据表 266 | /// 267 | /// 存储过程名 268 | /// 参数 269 | /// 返回的数据表 270 | public Task> CallProcedureAsync(string procName, params DbParameter[] parameters) 271 | { 272 | return m_BaseAccessor?.CallProcedureAsync(procName, parameters); 273 | } 274 | 275 | /// 276 | /// 关闭连接 277 | /// 278 | public void Close() 279 | { 280 | m_BaseAccessor?.Close(); 281 | } 282 | 283 | /// 284 | /// 按主键标记实体删除(即使传入的实体不是被追踪的实体,同主键的追踪实体依然会标记删除删除) 285 | /// 286 | /// 287 | /// 288 | /// 289 | public void Delete(T model) where T : class 290 | { 291 | m_BaseAccessor?.Delete(model); 292 | } 293 | 294 | /// 295 | /// 获取某个表的所有数据 296 | /// 297 | /// 298 | /// 299 | public IQueryable GetAll() where T : class 300 | { 301 | return m_BaseAccessor?.GetAll(); 302 | } 303 | 304 | /// 305 | /// 根据主键值来查询某条记录,单主键的表可直接输入主键,多主键的表注意主键次序 306 | /// 307 | /// 实体类型 308 | /// 主键值 309 | /// 返回主键值为传入值的实体 310 | public T GetByID(params object[] values) where T : class 311 | { 312 | return m_BaseAccessor?.GetByID(values); 313 | } 314 | 315 | /// 316 | /// 根据主键值来查询某条记录,单主键的表可直接输入主键,多主键的表注意主键次序 317 | /// 318 | /// 实体类型 319 | /// 主键值 320 | /// 返回主键值为传入值的实体 321 | public Task GetByIDAsync(params object[] values) where T : class 322 | { 323 | return m_BaseAccessor?.GetByIDAsync(values); 324 | } 325 | 326 | /// 327 | /// 获取条目数 328 | /// 329 | /// 查询的类 330 | /// 查询表达式 331 | /// 条目数 332 | public int GetCount(Expression> expression) where T : class 333 | { 334 | return m_BaseAccessor.GetCount(expression); 335 | } 336 | 337 | /// 338 | /// 获取条目数 339 | /// 340 | /// 查询的类 341 | /// 查询表达式 342 | /// 条目数 343 | public Task GetCountAsync(Expression> expression) where T : class 344 | { 345 | return m_BaseAccessor.GetCountAsync(expression); 346 | } 347 | 348 | /// 349 | /// 根据表达式进行查询返回结果 350 | /// 351 | /// 查询的类 352 | /// 查询表达式 353 | /// 354 | public IQueryable GetMany(Expression> expression) where T : class 355 | { 356 | return m_BaseAccessor.GetMany(expression); 357 | } 358 | 359 | /// 360 | /// 在数据库中进行分页查询 361 | /// 362 | /// 查询的类 363 | /// 每页条数 364 | /// 当前页 365 | /// 查询表达式 366 | /// 367 | public List GetPageList(int pageSize, int pageIdx, Expression> expression, Func orderExpression) where T : class 368 | { 369 | return m_BaseAccessor?.GetPageList(pageSize, pageIdx, expression, orderExpression); 370 | } 371 | 372 | /// 373 | /// 在数据库中进行分页查询 374 | /// 375 | /// 查询的类 376 | /// 页大小 377 | /// 页下标 378 | /// 查询表达式 379 | /// 排序表达式 380 | /// 分页查询结果 381 | public Task> GetPageListAsync(int pageSize, int pageIdx, Expression> expression, Func orderExpression) where T : class 382 | { 383 | return m_BaseAccessor?.GetPageListAsync(pageSize, pageIdx, expression, orderExpression); 384 | } 385 | 386 | /// 387 | /// 是否已关闭 388 | /// 389 | /// 390 | public bool IsClose() 391 | { 392 | return m_BaseAccessor.IsClose(); 393 | } 394 | 395 | /// 396 | /// 根据某个字段的值进行排序 397 | /// 398 | /// 排序后获得的集合的类型 399 | /// 排序字段的类型 400 | /// 字段表达式 如:basicInfo下根据caseID排序(s=>s.caseID) 401 | /// 是否升序 402 | /// 403 | public IEnumerable Order(Func orderExpression, bool isASC = false) where T : class 404 | { 405 | return m_BaseAccessor?.Order(orderExpression, isASC); 406 | } 407 | 408 | /// 409 | /// 提交对数据进行的处理,如无处理返回-1 410 | /// 411 | /// 412 | public int Save() 413 | { 414 | return m_BaseAccessor.Save(); 415 | } 416 | 417 | /// 418 | /// 提交对数据进行的处理,如无处理返回-1 419 | /// 420 | /// 421 | public Task SaveAsync() 422 | { 423 | return m_BaseAccessor.SaveAsync(); 424 | } 425 | 426 | /// 427 | /// 更新操作 428 | /// 429 | /// 430 | /// 431 | /// 432 | public void Update(T model) where T : class 433 | { 434 | m_BaseAccessor?.Update(model); 435 | } 436 | 437 | /// 438 | /// 根据主键更新指定字段,使用时需要注意EFCore的仓储机制可能会使数据库和缓存仓的数据不一致。 439 | /// 440 | /// 映射类 441 | /// 字段类型 442 | /// 更新的实体 443 | /// 要更新的属性, VisualStudio在这里有Bug, 不能智能显示类型属性, 但不影响使用 444 | /// 445 | public void Update(T model, Expression> property) where T : class 446 | { 447 | m_BaseAccessor?.Update(model, property); 448 | } 449 | 450 | /// 451 | /// 获取EFCore的DbContext 452 | /// 453 | /// DbContext 454 | public DbContext GetDbContext() 455 | { 456 | return m_BaseAccessor?.GetDbContext(); 457 | } 458 | #endregion 459 | 460 | #region IDisposable接口实现 461 | /// 462 | /// 释放EFCore的DbContext 463 | /// 464 | public void Dispose() 465 | { 466 | if (m_BaseAccessor != null) 467 | { 468 | if (!m_BaseAccessor.IsClose()) 469 | { 470 | m_BaseAccessor.Close(); 471 | } 472 | } 473 | } 474 | #endregion 475 | 476 | private TableAccessMapping GetTableName(Type mappingType, BaseDataAccessor helper) 477 | { 478 | var context = helper.GetDbContext(); 479 | var model = context.Model.FindEntityType(mappingType); 480 | 481 | if (model != null) 482 | { 483 | string table = model.GetTableName(); 484 | return new TableAccessMapping(mappingType, table); 485 | } 486 | else 487 | { 488 | throw new ArgumentException("Mapping type not found"); 489 | } 490 | } 491 | } 492 | } 493 | --------------------------------------------------------------------------------