├── EntityFramework.Extension ├── EntityFramework.Extension.Tests │ ├── packages.config │ ├── DemoDbContext.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── App.config │ ├── BaseContextTests.cs │ └── EntityFramework.Extension.Tests.csproj ├── EntityFramework.Extension │ ├── Entity │ │ ├── ISoftDelete.cs │ │ ├── IEntity.cs │ │ ├── IAuditionEntity.cs │ │ ├── ICreatorEntity.cs │ │ ├── IModifyEntity.cs │ │ ├── BaseEntity.cs │ │ ├── IDeletionEntity.cs │ │ ├── ModifyEntity.cs │ │ ├── CreatorEntity.cs │ │ ├── AuditionEntity.cs │ │ └── HandleState.cs │ ├── packages.config │ ├── App.config │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Config │ │ └── EntityFrameworkConfig.cs │ ├── EntityFramework.Extension.csproj │ ├── DbContext │ │ ├── BaseDbContext.cs │ │ └── DbContextHelper.cs │ └── Interceptor │ │ └── DbMasterSlaveCommandInterceptor.cs └── EntityFramework.Extension.sln ├── LICENSE ├── .gitattributes ├── README.md └── .gitignore /EntityFramework.Extension/EntityFramework.Extension.Tests/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /EntityFramework.Extension/EntityFramework.Extension/Entity/ISoftDelete.cs: -------------------------------------------------------------------------------- 1 | namespace EntityFramework.Extension.Entity 2 | { 3 | public interface ISoftDelete 4 | { 5 | bool IsDeleted { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /EntityFramework.Extension/EntityFramework.Extension/Entity/IEntity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace EntityFramework.Extension.Entity 8 | { 9 | public interface IEntity 10 | { 11 | TPrimaryKey Id { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /EntityFramework.Extension/EntityFramework.Extension/Entity/IAuditionEntity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace EntityFramework.Extension.Entity 8 | { 9 | public interface IAuditionEntity : ICreatorEntity, IModifyEntity, IDeletionEntity 10 | { 11 | 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /EntityFramework.Extension/EntityFramework.Extension/Entity/ICreatorEntity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace EntityFramework.Extension.Entity 8 | { 9 | public interface ICreatorEntity 10 | { 11 | DateTime CreateTime { get; set; } 12 | 13 | string CreatorId { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /EntityFramework.Extension/EntityFramework.Extension/Entity/IModifyEntity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace EntityFramework.Extension.Entity 8 | { 9 | public interface IModifyEntity 10 | { 11 | DateTime? LastModificationTime { get; set; } 12 | 13 | string LastModifierUserId { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /EntityFramework.Extension/EntityFramework.Extension/Entity/BaseEntity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace EntityFramework.Extension.Entity 8 | { 9 | public class BaseEntity : BaseEntity 10 | { 11 | } 12 | 13 | public class BaseEntity : IEntity 14 | { 15 | public T Id { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /EntityFramework.Extension/EntityFramework.Extension/Entity/IDeletionEntity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace EntityFramework.Extension.Entity 8 | { 9 | public interface IDeletionEntity : ISoftDelete 10 | { 11 | DateTime? DeletionTime { get; set; } 12 | 13 | string DeleterUserId { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /EntityFramework.Extension/EntityFramework.Extension/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /EntityFramework.Extension/EntityFramework.Extension/Entity/ModifyEntity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace EntityFramework.Extension.Entity 4 | { 5 | public class ModifyEntity : BaseEntity, ICreatorEntity, IModifyEntity 6 | { 7 | public DateTime CreateTime { get; set; } 8 | 9 | public string CreatorId { get; set; } 10 | 11 | public DateTime? LastModificationTime { get; set; } 12 | 13 | public string LastModifierUserId { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /EntityFramework.Extension/EntityFramework.Extension/Entity/CreatorEntity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace EntityFramework.Extension.Entity 8 | { 9 | public class CreatorEntity : BaseEntity, ICreatorEntity 10 | { 11 | public DateTime CreateTime { get; set; } 12 | public string CreatorId { get; set; } 13 | } 14 | 15 | public class CreatorEntity : CreatorEntity 16 | { 17 | 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /EntityFramework.Extension/EntityFramework.Extension/Entity/AuditionEntity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace EntityFramework.Extension.Entity 4 | { 5 | public class AuditionEntity : BaseEntity, IAuditionEntity 6 | { 7 | public DateTime CreateTime { get; set; } 8 | 9 | public string CreatorId { get; set; } 10 | 11 | public DateTime? LastModificationTime { get; set; } 12 | 13 | public string LastModifierUserId { get; set; } 14 | 15 | public DateTime? DeletionTime { get; set; } 16 | 17 | public string DeleterUserId { get; set; } 18 | 19 | public bool IsDeleted { get; set; } 20 | } 21 | 22 | public class AuditionEntity : AuditionEntity 23 | { } 24 | } 25 | -------------------------------------------------------------------------------- /EntityFramework.Extension/EntityFramework.Extension.Tests/DemoDbContext.cs: -------------------------------------------------------------------------------- 1 | using System.Data.Entity; 2 | using EntityFramework.Extension.Entity; 3 | 4 | namespace EntityFramework.Extension.Tests 5 | { 6 | public class DemoDbContext : BaseDbContext 7 | { 8 | static DemoDbContext() 9 | { 10 | GenerateViews(new DemoDbContext()); 11 | } 12 | 13 | /// 14 | /// 当前线程DbContext 15 | /// 16 | public static DemoDbContext CurrentDb => DbContextHelper.CurrentDbContext(); 17 | 18 | public IDbSet Users { get; set; } 19 | } 20 | 21 | public class User : BaseEntity 22 | { 23 | 24 | public string Name { get; set; } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /EntityFramework.Extension/EntityFramework.Extension/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 NeverCL 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /EntityFramework.Extension/EntityFramework.Extension/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("EntityFramework.Extension")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("EntityFramework.Extension")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | //将 ComVisible 设置为 false 将使此程序集中的类型 18 | //对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型, 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("f2d92c1c-25d2-465d-8696-944afd03050d")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, 33 | // 方法是按如下所示使用“*”: : 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("0.2.3.0")] 36 | [assembly: AssemblyFileVersion("0.2.3.0")] 37 | -------------------------------------------------------------------------------- /EntityFramework.Extension/EntityFramework.Extension.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("EntityFramework.Extension.Tests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("EntityFramework.Extension.Tests")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | //将 ComVisible 设置为 false 将使此程序集中的类型 18 | //对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型, 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("c5723c6b-0cb3-46b2-968f-60b6e3c41812")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, 33 | // 方法是按如下所示使用“*”: : 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /EntityFramework.Extension/EntityFramework.Extension.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.23107.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EntityFramework.Extension", "EntityFramework.Extension\EntityFramework.Extension.csproj", "{F2D92C1C-25D2-465D-8696-944AFD03050D}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EntityFramework.Extension.Tests", "EntityFramework.Extension.Tests\EntityFramework.Extension.Tests.csproj", "{C5723C6B-0CB3-46B2-968F-60B6E3C41812}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {F2D92C1C-25D2-465D-8696-944AFD03050D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {F2D92C1C-25D2-465D-8696-944AFD03050D}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {F2D92C1C-25D2-465D-8696-944AFD03050D}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {F2D92C1C-25D2-465D-8696-944AFD03050D}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {C5723C6B-0CB3-46B2-968F-60B6E3C41812}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {C5723C6B-0CB3-46B2-968F-60B6E3C41812}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {C5723C6B-0CB3-46B2-968F-60B6E3C41812}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {C5723C6B-0CB3-46B2-968F-60B6E3C41812}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /EntityFramework.Extension/EntityFramework.Extension.Tests/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /EntityFramework.Extension/EntityFramework.Extension/Entity/HandleState.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data.Entity; 3 | using System.Data.Entity.Infrastructure; 4 | using System.Security.Claims; 5 | using System.Threading; 6 | using Microsoft.AspNet.Identity; 7 | 8 | namespace EntityFramework.Extension.Entity 9 | { 10 | /// 11 | /// 处理状态 12 | /// 13 | public class HandleState 14 | { 15 | public static void Add(DbEntityEntry entry) 16 | { 17 | if (entry.Entity is ICreatorEntity) 18 | { 19 | var entity = entry.Entity as ICreatorEntity; 20 | entity.CreateTime = DateTime.Now; 21 | entity.CreatorId = GetUserId(); 22 | } 23 | } 24 | 25 | public static void Delete(DbEntityEntry entry) 26 | { 27 | if (entry.Entity is ISoftDelete) 28 | { 29 | entry.State = EntityState.Unchanged; 30 | var entity = entry.Entity as ISoftDelete; 31 | entity.IsDeleted = true; 32 | if (entry.Entity is IDeletionEntity) 33 | { 34 | var deletionEntity = entry.Entity as IDeletionEntity; 35 | deletionEntity.DeletionTime = DateTime.Now; 36 | deletionEntity.DeleterUserId = GetUserId(); 37 | } 38 | } 39 | } 40 | 41 | public static void Modify(DbEntityEntry entry) 42 | { 43 | if (entry.Entity is IModifyEntity) 44 | { 45 | var entity = entry.Entity as IModifyEntity; 46 | entity.LastModificationTime = DateTime.Now; 47 | entity.LastModifierUserId = GetUserId(); 48 | } 49 | } 50 | 51 | public static void Default(DbEntityEntry entry) 52 | { 53 | 54 | } 55 | 56 | private static string GetUserId() 57 | { 58 | var claimsPrincipal = Thread.CurrentPrincipal as ClaimsPrincipal; 59 | if (claimsPrincipal == null) 60 | return null; 61 | var claimsIdentity = claimsPrincipal.Identity as ClaimsIdentity; 62 | if (claimsIdentity == null) 63 | return null; 64 | return claimsIdentity.GetUserId(); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [高并发]EntityFramework之高性能扩展 2 | 3 | ## 目录 4 | - [简介](#简介) 5 | - [读写分离](#读写分离) 6 | - [指定字段更新](#指定字段更新) 7 | - [事务](#事务) 8 | - [Entity](#entity) 9 | 10 | ## 简介 11 | - 本EF扩展插件将持续更新:开源,敏捷,高性能。(由于EF Core暂未提供方便的钩子位置,暂无EF Core版本) 12 | 13 | - [EntityFramework.Extension代码](https://github.com/NeverCL/EntityFramework.Extension) (GitHub欢迎Fork) 14 | 15 | - [EntityFramework.Extension代码](https://www.nuget.org/packages/EntityFramework.Extension/) (Nuget:Install-Package EntityFramework.Extension) 16 | 17 | ## 读写分离 18 | 读写分离,支持可配置项的方式。同时支持权重的方式轮询。 19 | 20 | - 先看段配置文件 21 | ```xml 22 | 23 | 24 | 25 | 26 | 27 | 28 | ``` 29 | - `isSlaveRead` // 是否开启读写分离 30 | - `readConnstr` // 读库链接字符串 31 | - `slaves节点` // 当读库有多个时,通过`weight`支持权重轮询读库功能。(readConnstr配置不为空时,将忽略slaves节点) 32 | 33 | ## 指定字段更新 34 | 目前封装了3种形式的,指定字段更新方法。 35 | 36 | - 对象不存在上下文 37 | ```c# 38 | var user = new User { Id = 2, Name = Guid.NewGuid().ToString() }; 39 | DemoDbContext.CurrentDb.UpdateField(user, "Name"); 40 | ``` 41 | 42 | - 对象已存在上下文 43 | ```c# 44 | var user = new User { Id = 2, Name = Guid.NewGuid().ToString() }; 45 | DemoDbContext.CurrentDb.UpdateField(user, x => x.Id == 2, "Name"); 46 | ``` 47 | 48 | - 对象为IEntity,无论是否存在上下文均支持 49 | ```c# 50 | var user = new User { Id = 2, Name = Guid.NewGuid().ToString() }; 51 | DemoDbContext.CurrentDb.UpdateEntityField(user, "Name"); 52 | ``` 53 | ## 事务 54 | - 事务类型 55 | 在.NET 中,事务分SQLTransaction和TransactionScope。后者在MSDTC(Distributed Transaction Coordinator)开启的时候,支持分布式事务。 56 | - TransactionScopeOption 57 | - Required 58 | - 默认方式,如果存在环境事务,直接取环境事务,如果不存在,则创建新的 59 | - RequiresNew 60 | - 直接创建新的环境事务 61 | - Suppress 62 | - 取消当前区域环境事务 63 | 64 | - 隔离等级IsolationLevel 65 | - ReadUncommitted(读未提交) 66 | - 表示:未提交读。当事务A更新某条数据的时候,不容许其他事务来更新该数据,但可以进行读取操作 67 | - ReadCommitted(读提交) 68 | - 表示:提交读。当事务A更新数据时,不容许其他事务进行任何的操作包括读取,但事务A读取时,其他事务可以进行读取、更新 69 | - RepeatableRead 70 | - 表示:重复读。当事务A更新数据时,不容许其他事务进行任何的操作,但是当事务A进行读取的时候,其他事务只能读取,不能更新 71 | - Serializable 72 | - 表示:序列化。最严格的隔离级别,当然并发性也是最差的,事务必须依次进行。 73 | - 默认级别 74 | - Oracle read committed 75 | - SqlServer read committed 76 | - MySQL(InnoDB) Read-Repeatable 77 | 78 | - 事务特性(ACID) 79 | - 原子性(Atomicity) 80 | - 事务是数据库的逻辑工作单位,事务中的诸多操作要么全做要么全不做 81 | - 一致性(Consistency) 82 | - 事务执行结果必须是使数据库从一个一致性状态变到另一个一致性状态 83 | - 隔离性(Isolation) 84 | - 一个数据的执行不能被其他事务干扰 85 | - 持续性/永久性(Durability) 86 | - 一个事务一旦提交,它对数据库中的数据改变是永久性的 87 | 88 | 说了那么多,本插件对事务的支持: 89 | 90 | ```c# 91 | DemoDbContext.CurrentDb.TransExecute(x => { 92 | x.Users.Add(new User()); 93 | return x.SaveChanges(); 94 | }); 95 | ``` 96 | 97 | 针对事务,同时支持锁的读取功能 98 | ```c# 99 | var userList = DemoDbContext.CurrentDb.NoLockFunc(db => db.Users.ToList()); 100 | ``` 101 | 102 | ## Entity 103 | 类似ABP框架,提供了IEntity,ICreatorEntity,IModifyEntity,IAuditionEntity,IDeletionEntity等等 -------------------------------------------------------------------------------- /EntityFramework.Extension/EntityFramework.Extension/Config/EntityFrameworkConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Configuration; 3 | using System.Linq; 4 | 5 | namespace EntityFramework.Extension.Config 6 | { 7 | /// 8 | /// EF Config 9 | /// 10 | public sealed class EntityFrameworkConfig : ConfigurationSection 11 | { 12 | /// 13 | /// 主库连接字符串配置名 14 | /// 15 | [ConfigurationProperty("masterConnName", DefaultValue = "DefaultConnection")] 16 | public string MasterConnName 17 | { 18 | get { return this["masterConnName"].ToString(); } 19 | set { this["masterConnName"] = value; } 20 | } 21 | 22 | /// 23 | /// 从库读开关 24 | /// 25 | [ConfigurationProperty("isSlaveRead", DefaultValue = "false")] 26 | public bool IsSlaveRead 27 | { 28 | get { return (bool)this["isSlaveRead"]; } 29 | set { this["isSlaveRead"] = value; } 30 | } 31 | 32 | /// 33 | /// 从库连接字符串 34 | /// 35 | [ConfigurationProperty("readConnstr")] 36 | public string ReadConnstr 37 | { 38 | get 39 | { 40 | return (string)this["readConnstr"]; 41 | } 42 | set { this["readConnstr"] = value; } 43 | } 44 | 45 | /// 46 | /// 从库连接 47 | /// 48 | [ConfigurationProperty("slaves", IsDefaultCollection = true)] 49 | public ReaderConnectionCollection ReaderConnections 50 | { 51 | get { return (ReaderConnectionCollection)base["slaves"]; } 52 | } 53 | } 54 | 55 | 56 | public class ReaderConnectionCollection : ConfigurationElementCollection 57 | { 58 | protected override ConfigurationElement CreateNewElement() 59 | { 60 | return new ReaderConnection(); 61 | } 62 | 63 | protected override object GetElementKey(ConfigurationElement element) 64 | { 65 | return ((ReaderConnection)element).Name; 66 | } 67 | 68 | protected override string ElementName { get { return "slaves"; } } 69 | 70 | /// 71 | /// 获取所有键 72 | /// 73 | public IEnumerable AllKeys { get { return BaseGetAllKeys().Cast(); } } 74 | 75 | 76 | public new ReaderConnection this[string name] 77 | { 78 | get { return (ReaderConnection)BaseGet(name); } 79 | } 80 | } 81 | 82 | 83 | public class ReaderConnection : ConfigurationElement 84 | { 85 | [ConfigurationProperty("name")] 86 | public string Name 87 | { 88 | get { return (string)this["name"]; } 89 | set { this["name"] = value; } 90 | } 91 | 92 | [ConfigurationProperty("connectionString")] 93 | public string ConnectionString 94 | { 95 | get { return (string)this["connectionString"]; } 96 | set { this["connectionString"] = value; } 97 | } 98 | 99 | [ConfigurationProperty("providerName", DefaultValue = "System.Data.SqlClient")] 100 | public string ProviderName 101 | { 102 | get { return (string)this["providerName"]; } 103 | set { this["providerName"] = value; } 104 | } 105 | 106 | [ConfigurationProperty("weight", DefaultValue = 1)] 107 | public int Weight 108 | { 109 | get { return (int)this["weight"]; } 110 | set { this["weight"] = value; } 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /EntityFramework.Extension/EntityFramework.Extension.Tests/BaseContextTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data.Entity; 3 | using System.Linq; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | 6 | namespace EntityFramework.Extension.Tests 7 | { 8 | [TestClass] 9 | public class BaseContextTests 10 | { 11 | public BaseContextTests() 12 | { 13 | InitData(); 14 | } 15 | 16 | [TestMethod] 17 | public void InitData() 18 | { 19 | //DemoDbContext.CurrentDb.GenerateViews(); 20 | //var db = new DemoDbContext(); 21 | //db.Users.Add(new User 22 | //{ 23 | // Name = "name" 24 | //}); 25 | //db.SaveChanges(); 26 | } 27 | 28 | [TestMethod] 29 | public void TestSelectAndUpdate() 30 | { 31 | using (var trans = DemoDbContext.CurrentDb.Database.BeginTransaction()) 32 | { 33 | var user = DemoDbContext.CurrentDb.Users.Find(1); 34 | user.Name = Guid.NewGuid().ToString(); 35 | DemoDbContext.CurrentDb.SaveChanges(); 36 | } 37 | } 38 | 39 | [TestMethod] 40 | public void TestSelectMethod() 41 | { 42 | var user1 = DemoDbContext.CurrentDb.Users.FirstOrDefault(); 43 | var user2 = DemoDbContext.CurrentDb.Database.ExecuteSqlCommand("select * from Users"); 44 | var user3 = DemoDbContext.CurrentDb.Database.SqlQuery("select * from Users").ToList().FirstOrDefault(); 45 | //Assert.IsTrue(!string.IsNullOrEmpty(user.Name)); 46 | } 47 | 48 | /// 49 | /// 测试热处理 + 线程数据库 50 | /// 51 | [TestMethod] 52 | public void TestHotSelectMethod() 53 | { 54 | // 查询 55 | // 0.00.853 0.00.837 56 | // 0:01.143 0:01.054 0:01.168 57 | // 0:03.630 58 | for (int i = 0; i < 1000; i++) 59 | { 60 | DemoDbContext.CurrentDb.Users.ToList(); 61 | } 62 | } 63 | 64 | /// 65 | /// 无缓存读取 66 | /// 67 | [TestMethod] 68 | public void TestNoCacheSelectMethod() 69 | { 70 | var user1 = DemoDbContext.CurrentDb.Users.FirstOrDefault(); 71 | DemoDbContext.CurrentDb.Database.ExecuteSqlCommand("update Users set name = '" + Guid.NewGuid() + "' where id = 1"); 72 | user1 = DemoDbContext.CurrentDb.Users.FirstOrDefault(); 73 | var user2 = DemoDbContext.CurrentDb.Users.AsNoTracking().FirstOrDefault(); 74 | Assert.IsTrue(user1.Name != user2.Name); 75 | } 76 | 77 | /// 78 | /// 无锁读取 79 | /// 80 | [TestMethod] 81 | public void TestNoLockRead() 82 | { 83 | // begin tran .. 84 | var nolockList = DemoDbContext.CurrentDb.NoLockFunc(db => db.Users.ToList()); 85 | // commit tran .. 86 | //Assert.IsTrue(list.Count != nolockList.Count); 87 | } 88 | 89 | /// 90 | /// 普通修改 91 | /// 92 | [TestMethod] 93 | public void TestUpdate() 94 | { 95 | // 0.00.247 0.00.261 96 | var user = DemoDbContext.CurrentDb.Users.Find(2); 97 | user.Name = Guid.NewGuid().ToString(); 98 | DemoDbContext.CurrentDb.SaveChanges(); 99 | } 100 | 101 | /// 102 | /// 更新指定列 103 | /// 104 | [TestMethod] 105 | public void TestUpdateField() 106 | { 107 | // 0.00.187 0.00.181 108 | var user = new User { Id = 2, Name = Guid.NewGuid().ToString() }; 109 | DemoDbContext.CurrentDb.UpdateField(user, "Name"); 110 | DemoDbContext.CurrentDb.SaveChanges(); 111 | } 112 | 113 | /// 114 | /// 当对象存在上下文情况下 115 | /// 更新指定列 116 | /// 117 | [TestMethod] 118 | public void TestUpdateFieldAleady() 119 | { 120 | var user = DemoDbContext.CurrentDb.Users.Find(2); 121 | var user2 = new User { Id = 2, Name = Guid.NewGuid().ToString() }; 122 | DemoDbContext.CurrentDb.UpdateEntityField(user2, "Name"); 123 | DemoDbContext.CurrentDb.UpdateField(user2, x => x.Id == 2, "Name"); 124 | DemoDbContext.CurrentDb.SaveChanges(); 125 | } 126 | 127 | /// 128 | /// 多次查询 129 | /// 130 | [TestMethod] 131 | public void TestMultiSelect() 132 | { 133 | for (int i = 0; i < 10000; i++) 134 | { 135 | var user = DemoDbContext.CurrentDb.Users.AsNoTracking().ToList(); 136 | } 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /EntityFramework.Extension/EntityFramework.Extension/EntityFramework.Extension.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {F2D92C1C-25D2-465D-8696-944AFD03050D} 8 | Library 9 | Properties 10 | EntityFramework.Extension 11 | EntityFramework.Extension 12 | v4.6 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | ..\packages\Common.Logging.3.3.1\lib\net40\Common.Logging.dll 35 | True 36 | 37 | 38 | ..\packages\Common.Logging.Core.3.3.1\lib\net40\Common.Logging.Core.dll 39 | True 40 | 41 | 42 | ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll 43 | True 44 | 45 | 46 | ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll 47 | True 48 | 49 | 50 | ..\packages\Microsoft.AspNet.Identity.Core.2.2.1\lib\net45\Microsoft.AspNet.Identity.Core.dll 51 | True 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 96 | -------------------------------------------------------------------------------- /EntityFramework.Extension/EntityFramework.Extension/DbContext/BaseDbContext.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Data; 3 | using System.Data.Common; 4 | using System.Data.Entity; 5 | using System.Data.Entity.Core.Mapping; 6 | using System.Data.Entity.Core.Metadata.Edm; 7 | using System.Data.Entity.Infrastructure; 8 | using System.Data.Entity.Infrastructure.Interception; 9 | using System.Data.Entity.Validation; 10 | using System.Threading; 11 | using System.Threading.Tasks; 12 | using Common.Logging; 13 | using EntityFramework.Extension.Entity; 14 | using EntityFramework.Extension.Interceptor; 15 | 16 | namespace EntityFramework.Extension 17 | { 18 | public class BaseDbContext : DbContext 19 | { 20 | #region ctor 21 | static BaseDbContext() 22 | { 23 | //MasterSlave(); 24 | } 25 | 26 | protected BaseDbContext() : this("DefaultConnection") 27 | { 28 | 29 | } 30 | 31 | protected BaseDbContext(DbConnection connection) : base(connection, true) 32 | { 33 | } 34 | 35 | protected BaseDbContext(string connStr) 36 | : base(connStr) 37 | { 38 | } 39 | 40 | /// 41 | /// EF 热处理 42 | /// 43 | /// 44 | protected static void GenerateViews(DbContext db) 45 | { 46 | using (db) 47 | { 48 | var objectContext = ((IObjectContextAdapter)db).ObjectContext; 49 | var mappingCollection = (StorageMappingItemCollection)objectContext.MetadataWorkspace.GetItemCollection(DataSpace.CSSpace); 50 | mappingCollection.GenerateViews(new List()); 51 | } 52 | } 53 | #endregion 54 | 55 | #region HandleState 56 | protected virtual void ApplyConcepts() 57 | { 58 | foreach (DbEntityEntry entry in this.ChangeTracker.Entries()) 59 | { 60 | switch (entry.State) 61 | { 62 | case EntityState.Added: 63 | HandleAdd(entry); 64 | continue; 65 | case EntityState.Deleted: 66 | HandleDelete(entry); 67 | continue; 68 | case EntityState.Modified: 69 | HandleModify(entry); 70 | if (entry.Entity is ISoftDelete && (entry.Entity as ISoftDelete).IsDeleted) 71 | { 72 | HandleDelete(entry); 73 | } 74 | continue; 75 | default: 76 | HandleDefault(entry); 77 | LogManager.GetLogger(GetType()).Debug("HandleDefault" + entry); 78 | continue; 79 | } 80 | } 81 | } 82 | 83 | protected virtual void HandleDefault(DbEntityEntry entry) 84 | { 85 | HandleState.Default(entry); 86 | } 87 | 88 | protected virtual void HandleAdd(DbEntityEntry entry) 89 | { 90 | HandleState.Add(entry); 91 | } 92 | 93 | protected virtual void HandleDelete(DbEntityEntry entry) 94 | { 95 | HandleState.Delete(entry); 96 | } 97 | 98 | protected virtual void HandleModify(DbEntityEntry entry) 99 | { 100 | HandleState.Modify(entry); 101 | } 102 | #endregion 103 | 104 | #region SaveChanges 105 | public override int SaveChanges() 106 | { 107 | try 108 | { 109 | ApplyConcepts(); 110 | return base.SaveChanges(); 111 | } 112 | catch (DbEntityValidationException ex) 113 | { 114 | LogDbEntityValidationException(ex); 115 | throw; 116 | } 117 | } 118 | 119 | public override Task SaveChangesAsync() 120 | { 121 | try 122 | { 123 | ApplyConcepts(); 124 | return base.SaveChangesAsync(); 125 | } 126 | catch (DbEntityValidationException ex) 127 | { 128 | LogDbEntityValidationException(ex); 129 | throw; 130 | } 131 | } 132 | 133 | public override Task SaveChangesAsync(CancellationToken cancellationToken) 134 | { 135 | try 136 | { 137 | ApplyConcepts(); 138 | return base.SaveChangesAsync(cancellationToken); 139 | } 140 | catch (DbEntityValidationException ex) 141 | { 142 | LogDbEntityValidationException(ex); 143 | throw; 144 | } 145 | } 146 | #endregion 147 | 148 | #region MasterSlave 149 | /// 150 | /// 主从复制 151 | /// 152 | protected static void MasterSlave() 153 | { 154 | DbInterception.Add(new DbMasterSlaveCommandInterceptor()); 155 | } 156 | #endregion 157 | 158 | #region Log 159 | protected virtual void LogDbEntityValidationException(DbEntityValidationException exception) 160 | { 161 | LogManager.GetLogger(GetType()).Error(exception); 162 | } 163 | #endregion 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /EntityFramework.Extension/EntityFramework.Extension.Tests/EntityFramework.Extension.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | {C5723C6B-0CB3-46B2-968F-60B6E3C41812} 7 | Library 8 | Properties 9 | EntityFramework.Extension.Tests 10 | EntityFramework.Extension.Tests 11 | v4.6 12 | 512 13 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 14 | 10.0 15 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 16 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages 17 | False 18 | UnitTest 19 | 20 | 21 | true 22 | full 23 | false 24 | bin\Debug\ 25 | DEBUG;TRACE 26 | prompt 27 | 4 28 | 29 | 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | 37 | 38 | 39 | ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll 40 | True 41 | 42 | 43 | ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll 44 | True 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | {F2D92C1C-25D2-465D-8696-944AFD03050D} 69 | EntityFramework.Extension 70 | 71 | 72 | 73 | 74 | Always 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | False 83 | 84 | 85 | False 86 | 87 | 88 | False 89 | 90 | 91 | False 92 | 93 | 94 | 95 | 96 | 97 | 98 | 105 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # .NET Core 46 | project.lock.json 47 | project.fragment.lock.json 48 | artifacts/ 49 | **/Properties/launchSettings.json 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # Visual Studio code coverage results 117 | *.coverage 118 | *.coveragexml 119 | 120 | # NCrunch 121 | _NCrunch_* 122 | .*crunch*.local.xml 123 | nCrunchTemp_* 124 | 125 | # MightyMoose 126 | *.mm.* 127 | AutoTest.Net/ 128 | 129 | # Web workbench (sass) 130 | .sass-cache/ 131 | 132 | # Installshield output folder 133 | [Ee]xpress/ 134 | 135 | # DocProject is a documentation generator add-in 136 | DocProject/buildhelp/ 137 | DocProject/Help/*.HxT 138 | DocProject/Help/*.HxC 139 | DocProject/Help/*.hhc 140 | DocProject/Help/*.hhk 141 | DocProject/Help/*.hhp 142 | DocProject/Help/Html2 143 | DocProject/Help/html 144 | 145 | # Click-Once directory 146 | publish/ 147 | 148 | # Publish Web Output 149 | *.[Pp]ublish.xml 150 | *.azurePubxml 151 | # TODO: Comment the next line if you want to checkin your web deploy settings 152 | # but database connection strings (with potential passwords) will be unencrypted 153 | *.pubxml 154 | *.publishproj 155 | 156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 157 | # checkin your Azure Web App publish settings, but sensitive information contained 158 | # in these scripts will be unencrypted 159 | PublishScripts/ 160 | 161 | # NuGet Packages 162 | *.nupkg 163 | # The packages folder can be ignored because of Package Restore 164 | **/packages/* 165 | # except build/, which is used as an MSBuild target. 166 | !**/packages/build/ 167 | # Uncomment if necessary however generally it will be regenerated when needed 168 | #!**/packages/repositories.config 169 | # NuGet v3's project.json files produces more ignorable files 170 | *.nuget.props 171 | *.nuget.targets 172 | 173 | # Microsoft Azure Build Output 174 | csx/ 175 | *.build.csdef 176 | 177 | # Microsoft Azure Emulator 178 | ecf/ 179 | rcf/ 180 | 181 | # Windows Store app package directories and files 182 | AppPackages/ 183 | BundleArtifacts/ 184 | Package.StoreAssociation.xml 185 | _pkginfo.txt 186 | 187 | # Visual Studio cache files 188 | # files ending in .cache can be ignored 189 | *.[Cc]ache 190 | # but keep track of directories ending in .cache 191 | !*.[Cc]ache/ 192 | 193 | # Others 194 | ClientBin/ 195 | ~$* 196 | *~ 197 | *.dbmdl 198 | *.dbproj.schemaview 199 | *.jfm 200 | *.pfx 201 | *.publishsettings 202 | orleans.codegen.cs 203 | 204 | # Since there are multiple workflows, uncomment next line to ignore bower_components 205 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 206 | #bower_components/ 207 | 208 | # RIA/Silverlight projects 209 | Generated_Code/ 210 | 211 | # Backup & report files from converting an old project file 212 | # to a newer Visual Studio version. Backup files are not needed, 213 | # because we have git ;-) 214 | _UpgradeReport_Files/ 215 | Backup*/ 216 | UpgradeLog*.XML 217 | UpgradeLog*.htm 218 | 219 | # SQL Server files 220 | *.mdf 221 | *.ldf 222 | *.ndf 223 | 224 | # Business Intelligence projects 225 | *.rdl.data 226 | *.bim.layout 227 | *.bim_*.settings 228 | 229 | # Microsoft Fakes 230 | FakesAssemblies/ 231 | 232 | # GhostDoc plugin setting file 233 | *.GhostDoc.xml 234 | 235 | # Node.js Tools for Visual Studio 236 | .ntvs_analysis.dat 237 | node_modules/ 238 | 239 | # Typescript v1 declaration files 240 | typings/ 241 | 242 | # Visual Studio 6 build log 243 | *.plg 244 | 245 | # Visual Studio 6 workspace options file 246 | *.opt 247 | 248 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 249 | *.vbw 250 | 251 | # Visual Studio LightSwitch build output 252 | **/*.HTMLClient/GeneratedArtifacts 253 | **/*.DesktopClient/GeneratedArtifacts 254 | **/*.DesktopClient/ModelManifest.xml 255 | **/*.Server/GeneratedArtifacts 256 | **/*.Server/ModelManifest.xml 257 | _Pvt_Extensions 258 | 259 | # Paket dependency manager 260 | .paket/paket.exe 261 | paket-files/ 262 | 263 | # FAKE - F# Make 264 | .fake/ 265 | 266 | # JetBrains Rider 267 | .idea/ 268 | *.sln.iml 269 | 270 | # CodeRush 271 | .cr/ 272 | 273 | # Python Tools for Visual Studio (PTVS) 274 | __pycache__/ 275 | *.pyc 276 | 277 | # Cake - Uncomment if you are using it 278 | # tools/** 279 | # !tools/packages.config 280 | 281 | # Telerik's JustMock configuration file 282 | *.jmconfig 283 | 284 | # BizTalk build output 285 | *.btp.cs 286 | *.btm.cs 287 | *.odx.cs 288 | *.xsd.cs 289 | -------------------------------------------------------------------------------- /EntityFramework.Extension/EntityFramework.Extension/DbContext/DbContextHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Data.Entity; 5 | using System.Data.Entity.Core.Mapping; 6 | using System.Data.Entity.Core.Metadata.Edm; 7 | using System.Data.Entity.Infrastructure; 8 | using System.Linq; 9 | using System.Runtime.Remoting.Messaging; 10 | using System.Transactions; 11 | using Common.Logging; 12 | using EntityFramework.Extension.Entity; 13 | using IsolationLevel = System.Transactions.IsolationLevel; 14 | 15 | namespace EntityFramework.Extension 16 | { 17 | public static class DbContextHelper 18 | { 19 | #region UpdateField 20 | /// 21 | /// 更新指定字段 22 | /// 忽略其他锁 直接更新字段 23 | /// 兼容对象存在上下文处理 24 | /// 25 | /// 26 | /// 27 | /// 28 | /// 29 | /// 30 | /// 31 | public static void UpdateField(this TDbContext dbContext, T entity, Func isSame, params string[] propertyNames) where TDbContext : DbContext, new() where T : class 32 | { 33 | var db = CurrentDbContext(); 34 | db.Entry(entity).State = EntityState.Detached; 35 | var attachedEntity = db.Set().Local.SingleOrDefault(isSame); 36 | if (attachedEntity != null) 37 | { 38 | // 对象存在上下文中 39 | var attachedEntry = db.Entry(attachedEntity); 40 | attachedEntry.CurrentValues.SetValues(entity); 41 | } 42 | else 43 | { 44 | // 对象不存在上下文中 45 | UpdateField(dbContext, entity, propertyNames); 46 | } 47 | } 48 | 49 | /// 50 | /// 更新指定字段 51 | /// 忽略其他锁 直接更新字段 52 | /// 当对象存在上下文会报错 53 | /// 54 | /// 55 | /// 56 | /// 57 | /// 58 | /// 59 | public static void UpdateField(this TDbContext dbContext, T entity, params string[] propertyNames) where TDbContext : DbContext, new() where T : class 60 | { 61 | var db = CurrentDbContext(); 62 | db.Set().Attach(entity); 63 | var setEntry = ((IObjectContextAdapter)db).ObjectContext.ObjectStateManager.GetObjectStateEntry(entity); 64 | foreach (var propertyName in propertyNames) 65 | { 66 | setEntry.SetModifiedProperty(propertyName); 67 | } 68 | } 69 | 70 | /// 71 | /// 更新指定字段 72 | /// 73 | /// 74 | /// 75 | /// 76 | /// 实现 IEntity《TPrimaryKey》的实体 77 | /// 78 | public static void UpdateEntityField(this TDbContext dbContext, IEntity entity, params string[] propertyNames) 79 | where TDbContext : DbContext, new() 80 | { 81 | var db = CurrentDbContext(); 82 | db.Entry(entity).State = EntityState.Detached; 83 | var type = entity.GetType(); 84 | var attachedEntity = db.Set(type).Find(entity.Id); 85 | if (attachedEntity != null) 86 | { 87 | // 对象存在上下文中 88 | var attachedEntry = db.Entry(attachedEntity); 89 | attachedEntry.CurrentValues.SetValues(entity); 90 | } 91 | else 92 | { 93 | // 对象不存在上下文中 94 | UpdateField(dbContext, entity, propertyNames); 95 | } 96 | } 97 | #endregion 98 | 99 | #region Thread 100 | /// 101 | /// 线程级 缓存 数据库 102 | /// 103 | /// 104 | /// 105 | public static TDbContext CurrentDbContext() where TDbContext : DbContext, new() 106 | { 107 | var name = typeof(TDbContext).FullName; 108 | var db = CallContext.GetData(name) as TDbContext; 109 | if (db == null) 110 | { 111 | db = new TDbContext(); 112 | CallContext.SetData(name, db); 113 | } 114 | return db; 115 | } 116 | #endregion 117 | 118 | #region GenerateViews 119 | /// 120 | /// EF 热处理 121 | /// 122 | /// 123 | public static void GenerateViews(this DbContext db) 124 | { 125 | var objectContext = ((IObjectContextAdapter)db).ObjectContext; 126 | var mappingCollection = (StorageMappingItemCollection)objectContext.MetadataWorkspace.GetItemCollection(DataSpace.CSSpace); 127 | mappingCollection.GenerateViews(new List()); 128 | } 129 | #endregion 130 | 131 | #region NoLockFunc 132 | /// 133 | /// 事务期间读取 和 修改 可变数据 134 | /// 主要用于nolock读取 135 | /// 136 | /// 137 | /// 138 | /// 139 | /// 140 | /// 141 | public static T NoLockFunc(this TDbContext dbContext, Func func) where TDbContext : DbContext 142 | { 143 | var transactionOptions = new TransactionOptions 144 | { 145 | IsolationLevel = IsolationLevel.ReadUncommitted 146 | }; 147 | using (new TransactionScope(TransactionScopeOption.Required, transactionOptions)) 148 | { 149 | try 150 | { 151 | return func(dbContext); 152 | } 153 | catch (Exception ex) 154 | { 155 | LogManager.GetLogger(dbContext.GetType()).Error(ex); 156 | throw; 157 | } 158 | } 159 | } 160 | #endregion 161 | 162 | #region TransExecute 163 | /// 164 | /// 如果存在环境事务,直接取环境事务,如果不存在,则创建新的事务执行 165 | /// 省略事务提交步骤 166 | /// 167 | public static T TransExecute(this TDbContext dbContext, Func func, TransactionScopeOption transactionScopeOption = TransactionScopeOption.Required) where TDbContext : DbContext 168 | { 169 | using (var trans = new TransactionScope(transactionScopeOption)) 170 | { 171 | var rst = func(dbContext); 172 | trans.Complete(); 173 | return rst; 174 | } 175 | } 176 | #endregion 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /EntityFramework.Extension/EntityFramework.Extension/Interceptor/DbMasterSlaveCommandInterceptor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Data; 5 | using System.Data.Common; 6 | using System.Data.Entity.Infrastructure.Interception; 7 | using System.Diagnostics; 8 | using System.Linq; 9 | using Common.Logging; 10 | using EntityFramework.Extension.Config; 11 | 12 | namespace EntityFramework.Extension.Interceptor 13 | { 14 | public class DbMasterSlaveCommandInterceptor : DbCommandInterceptor 15 | { 16 | const string Typename = "DbMasterSlaveCommandInterceptor"; 17 | 18 | //private readonly string _masterConnectionString = @"Data Source=(localdb)\test;Initial Catalog=masterdb;Integrated Security=True;"; 19 | private readonly string _masterConnectionString; 20 | 21 | private readonly EntityFrameworkConfig _config; 22 | 23 | #region weight 24 | private List _listWeight; 25 | private int maxWeight = -1; 26 | #endregion 27 | 28 | public DbMasterSlaveCommandInterceptor() : this((EntityFrameworkConfig)ConfigurationManager.GetSection("entityFrameworkConfig")) 29 | { 30 | 31 | } 32 | 33 | public DbMasterSlaveCommandInterceptor(EntityFrameworkConfig config) 34 | { 35 | this._config = config; 36 | _masterConnectionString = ConfigurationManager.ConnectionStrings[config.MasterConnName].ConnectionString; 37 | LogManager.GetLogger(Typename).Debug("DbMasterSlaveCommandInterceptor()"); 38 | } 39 | 40 | #region 切换数据库链接 41 | 42 | void ChangeToReadConnectionString(DbInterceptionContext interceptionContext) 43 | { 44 | lock ("IsReadThreadSlave") 45 | { 46 | string connectionString; 47 | if (!string.IsNullOrEmpty(_config.ReadConnstr)) 48 | connectionString = _config.ReadConnstr; 49 | else 50 | connectionString = GetWeightConnectString(_config.ReaderConnections); 51 | UpdateConnectionString(interceptionContext, connectionString); 52 | } 53 | } 54 | 55 | void ChangeToWriteConnectionString(DbCommandInterceptionContext interceptionContext) 56 | { 57 | lock ("IsWriteThreadSlave") 58 | { 59 | UpdateConnectionString(interceptionContext, this._masterConnectionString); 60 | } 61 | } 62 | 63 | /// 64 | /// 修改数据库连接 65 | /// 66 | /// 67 | /// 68 | private void UpdateConnectionString(DbInterceptionContext interceptionContext, string connectionString) 69 | { 70 | foreach (var context in interceptionContext.DbContexts) 71 | { 72 | //this.UpdateConnectionString(context.Database.Connection, connectionString); 73 | this.UpdateConnectionStringIfNeed(context.Database.Connection, connectionString); 74 | } 75 | } 76 | 77 | /// 78 | /// 获取权重连接 79 | /// 80 | /// 81 | /// 82 | private string GetWeightConnectString(ReaderConnectionCollection configReaderConnections) 83 | { 84 | if (maxWeight == -1) 85 | { 86 | maxWeight = 0; 87 | _listWeight = new List(); 88 | foreach (ReaderConnection configReaderConnection in configReaderConnections) 89 | { 90 | maxWeight += configReaderConnection.Weight; 91 | for (int i = 0; i < configReaderConnection.Weight; i++) 92 | { 93 | _listWeight.Add(configReaderConnection.Name); 94 | } 95 | } 96 | } 97 | var value = new Random().Next(maxWeight); 98 | var key = _listWeight[value]; 99 | var connectionString = configReaderConnections[key].ConnectionString; 100 | return connectionString; 101 | } 102 | 103 | /// 104 | /// 根据需要 修改数据库连接 105 | /// 106 | /// 107 | /// 108 | private void UpdateConnectionStringIfNeed(DbConnection conn, string connectionString) 109 | { 110 | if (!this.ConnectionStringCompare(conn, connectionString)) 111 | { 112 | ConnectionState state = conn.State; 113 | if (state == ConnectionState.Open) 114 | conn.Close(); 115 | 116 | conn.ConnectionString = connectionString; 117 | 118 | if (state == ConnectionState.Open) 119 | conn.Open(); 120 | } 121 | } 122 | 123 | /// 124 | /// 判断数据库连接 是否一致 125 | /// 126 | /// 127 | /// 128 | /// true:一致 129 | private bool ConnectionStringCompare(DbConnection conn, string connectionString) 130 | { 131 | DbProviderFactory factory = DbProviderFactories.GetFactory(conn); 132 | 133 | DbConnectionStringBuilder a = factory.CreateConnectionStringBuilder(); 134 | a.ConnectionString = conn.ConnectionString; 135 | 136 | DbConnectionStringBuilder b = factory.CreateConnectionStringBuilder(); 137 | b.ConnectionString = connectionString; 138 | 139 | return a.EquivalentTo(b); 140 | } 141 | #endregion 142 | 143 | /// 144 | /// Linq 生成的select,insert + Database.SqlQuery("select * from Users").ToList(); 145 | /// prompt:在select语句中DbCommand.Transaction为null,而ef会为每个insert添加一个DbCommand.Transaction进行包裹 146 | /// 147 | /// 148 | /// 149 | public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext interceptionContext) 150 | { 151 | if (_config.IsSlaveRead && !command.CommandText.StartsWith("insert", StringComparison.CurrentCultureIgnoreCase) && command.Transaction == null) 152 | { 153 | ChangeToReadConnectionString(interceptionContext); 154 | } 155 | LogManager.GetLogger(Typename).Debug(command.CommandText); 156 | } 157 | 158 | /// 159 | /// Linq 生成的update,delete + Database.ExecuteSqlCommand 160 | /// 161 | /// 162 | /// 163 | public override void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext interceptionContext) 164 | { 165 | ChangeToWriteConnectionString(interceptionContext); 166 | LogManager.GetLogger(Typename).Debug(command.CommandText); 167 | } 168 | 169 | /// 170 | /// 执行sql语句,并返回第一行第一列,没有找到返回null,如果数据库中值为null,则返回 DBNull.Value 171 | /// 172 | /// 173 | /// 174 | public override void ScalarExecuting(DbCommand command, DbCommandInterceptionContext interceptionContext) 175 | { 176 | ChangeToReadConnectionString(interceptionContext); 177 | LogManager.GetLogger(Typename).Debug(command.CommandText); 178 | } 179 | } 180 | 181 | } 182 | --------------------------------------------------------------------------------