├── 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 |
--------------------------------------------------------------------------------