├── .gitattributes ├── .gitignore ├── CHANGELOG.md ├── EFRepository.Tests ├── App.config ├── EFRepository.Tests.csproj ├── Hooks │ ├── SoftDelete.feature │ ├── SoftDelete.feature.cs │ ├── SoftDeleteSteps.cs │ ├── SystemInfo.feature │ ├── SystemInfo.feature.cs │ └── SystemInfoSteps.cs ├── Properties │ └── AssemblyInfo.cs ├── Repositories │ ├── GenericRepository.feature │ ├── GenericRepository.feature.cs │ └── GenericRepositorySteps.cs ├── TestClasses │ ├── SoftDeleteData.cs │ ├── SoftDeleteRepository.cs │ ├── SystemInfoData.cs │ ├── SystemInfoRepository.cs │ ├── TestData.cs │ ├── TestDbContext.cs │ └── TestRepository.cs └── packages.config ├── EFRepository.sln ├── EFRepository ├── App.config ├── DatetimeHelper.cs ├── EFRepository.csproj ├── GenericRepository.cs ├── Hooks │ ├── HookContext.cs │ ├── IPostActionHook.cs │ ├── IPostLoadHook.cs │ ├── SoftDeletePostActionHook.cs │ ├── SoftDeletePostLoadHook.cs │ └── SystemInfoPostActionHook.cs ├── IDatetimeHelper.cs ├── IEntity.cs ├── IRepository.cs ├── ISoftDelete.cs ├── ISystemInfo.cs ├── IUserHelper.cs ├── Properties │ └── AssemblyInfo.cs ├── UnitOfWork.cs └── packages.config ├── LICENSE ├── README.md ├── appveyor.yml ├── nuget └── KirkChen.EFRepository.nuspec ├── package.json └── tools ├── build.bat ├── buildfinish.bat ├── buildpullrequest.bat ├── generateDocs.bat ├── packNuget.bat ├── packPrereleaseNuget.bat └── pushDocs.ps1 /.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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | [Xx]64/ 19 | [Xx]86/ 20 | [Bb]uild/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | [Tt]estDocs/ 34 | 35 | # NUNIT 36 | *.VisualState.xml 37 | TestResult.xml 38 | 39 | # OpenCover 40 | CodeCoverage.xml 41 | 42 | # SonarQube 43 | .sonarqube/ 44 | 45 | # Build Results of an ATL Project 46 | [Dd]ebugPS/ 47 | [Rr]eleasePS/ 48 | dlldata.c 49 | 50 | # DNX 51 | project.lock.json 52 | artifacts/ 53 | 54 | *_i.c 55 | *_p.c 56 | *_i.h 57 | *.ilk 58 | *.meta 59 | *.obj 60 | *.pch 61 | *.pdb 62 | *.pgc 63 | *.pgd 64 | *.rsp 65 | *.sbr 66 | *.tlb 67 | *.tli 68 | *.tlh 69 | *.tmp 70 | *.tmp_proj 71 | *.log 72 | *.vspscc 73 | *.vssscc 74 | .builds 75 | *.pidb 76 | *.svclog 77 | *.scc 78 | 79 | # Chutzpah Test files 80 | _Chutzpah* 81 | 82 | # Visual C++ cache files 83 | ipch/ 84 | *.aps 85 | *.ncb 86 | *.opendb 87 | *.opensdf 88 | *.sdf 89 | *.cachefile 90 | *.VC.db 91 | 92 | # Visual Studio profiler 93 | *.psess 94 | *.vsp 95 | *.vspx 96 | *.sap 97 | 98 | # TFS 2012 Local Workspace 99 | $tf/ 100 | 101 | # Guidance Automation Toolkit 102 | *.gpState 103 | 104 | # ReSharper is a .NET coding add-in 105 | _ReSharper*/ 106 | *.[Rr]e[Ss]harper 107 | *.DotSettings.user 108 | 109 | # JustCode is a .NET coding add-in 110 | .JustCode 111 | 112 | # TeamCity is a build add-in 113 | _TeamCity* 114 | 115 | # DotCover is a Code Coverage Tool 116 | *.dotCover 117 | 118 | # NCrunch 119 | _NCrunch_* 120 | .*crunch*.local.xml 121 | nCrunchTemp_* 122 | 123 | # MightyMoose 124 | *.mm.* 125 | AutoTest.Net/ 126 | 127 | # Web workbench (sass) 128 | .sass-cache/ 129 | 130 | # Installshield output folder 131 | [Ee]xpress/ 132 | 133 | # DocProject is a documentation generator add-in 134 | DocProject/buildhelp/ 135 | DocProject/Help/*.HxT 136 | DocProject/Help/*.HxC 137 | DocProject/Help/*.hhc 138 | DocProject/Help/*.hhk 139 | DocProject/Help/*.hhp 140 | DocProject/Help/Html2 141 | DocProject/Help/html 142 | 143 | # Click-Once directory 144 | publish/ 145 | 146 | # Publish Web Output 147 | *.[Pp]ublish.xml 148 | *.azurePubxml 149 | 150 | # TODO: Un-comment the next line if you do not want to checkin 151 | # your web deploy settings because they may include unencrypted 152 | # passwords 153 | #*.pubxml 154 | *.publishproj 155 | 156 | # NuGet Packages 157 | *.nupkg 158 | # The packages folder can be ignored because of Package Restore 159 | **/packages/* 160 | # except build/, which is used as an MSBuild target. 161 | !**/packages/build/ 162 | # Uncomment if necessary however generally it will be regenerated when needed 163 | #!**/packages/repositories.config 164 | # NuGet v3's project.json files produces more ignoreable files 165 | *.nuget.props 166 | *.nuget.targets 167 | 168 | # Microsoft Azure Build Output 169 | csx/ 170 | *.build.csdef 171 | 172 | # Microsoft Azure Emulator 173 | ecf/ 174 | rcf/ 175 | 176 | # Microsoft Azure ApplicationInsights config file 177 | ApplicationInsights.config 178 | 179 | # Windows Store app package directory 180 | AppPackages/ 181 | BundleArtifacts/ 182 | 183 | # Visual Studio cache files 184 | # files ending in .cache can be ignored 185 | *.[Cc]ache 186 | # but keep track of directories ending in .cache 187 | !*.[Cc]ache/ 188 | 189 | # Others 190 | ClientBin/ 191 | [Ss]tyle[Cc]op.* 192 | ~$* 193 | *~ 194 | *.dbmdl 195 | *.dbproj.schemaview 196 | *.pfx 197 | *.publishsettings 198 | node_modules/ 199 | orleans.codegen.cs 200 | 201 | # RIA/Silverlight projects 202 | Generated_Code/ 203 | 204 | # Backup & report files from converting an old project file 205 | # to a newer Visual Studio version. Backup files are not needed, 206 | # because we have git ;-) 207 | _UpgradeReport_Files/ 208 | Backup*/ 209 | UpgradeLog*.XML 210 | UpgradeLog*.htm 211 | 212 | # SQL Server files 213 | *.mdf 214 | *.ldf 215 | 216 | # Business Intelligence projects 217 | *.rdl.data 218 | *.bim.layout 219 | *.bim_*.settings 220 | 221 | # Microsoft Fakes 222 | FakesAssemblies/ 223 | 224 | # GhostDoc plugin setting file 225 | *.GhostDoc.xml 226 | 227 | # Node.js Tools for Visual Studio 228 | .ntvs_analysis.dat 229 | 230 | # Visual Studio 6 build log 231 | *.plg 232 | 233 | # Visual Studio 6 workspace options file 234 | *.opt 235 | 236 | # Visual Studio LightSwitch build output 237 | **/*.HTMLClient/GeneratedArtifacts 238 | **/*.DesktopClient/GeneratedArtifacts 239 | **/*.DesktopClient/ModelManifest.xml 240 | **/*.Server/GeneratedArtifacts 241 | **/*.Server/ModelManifest.xml 242 | _Pvt_Extensions 243 | 244 | # LightSwitch generated files 245 | GeneratedArtifacts/ 246 | ModelManifest.xml 247 | 248 | # Paket dependency manager 249 | .paket/paket.exe 250 | 251 | # FAKE - F# Make 252 | .fake/ -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | # 1.0.0 (2016-08-07) 3 | 4 | 5 | ### Features 6 | 7 | * **hook:** Implement system infomation hook ([400c0dd](https://github.com/kirkchen/EFRepository/commit/400c0dd)) 8 | * **hook:** Implement soft delete ([9c7f7cb](https://github.com/kirkchen/EFRepository/commit/9c7f7cb)) 9 | * **library:** Initial project ([48d7abe](https://github.com/kirkchen/EFRepository/commit/48d7abe)) 10 | * **library:** Install required packages ([30438c5](https://github.com/kirkchen/EFRepository/commit/30438c5)) 11 | * **repository:** Add add range funciton to interface ([78324bb](https://github.com/kirkchen/EFRepository/commit/78324bb)) 12 | * **repository:** Implement add range ([8191654](https://github.com/kirkchen/EFRepository/commit/8191654)) 13 | * **repository:** Implement delete feature ([bf32d82](https://github.com/kirkchen/EFRepository/commit/bf32d82)) 14 | * **repository:** Implement get data by id feature ([86a7993](https://github.com/kirkchen/EFRepository/commit/86a7993)) 15 | * **repository:** Implement get data with condition feature ([f4f16a9](https://github.com/kirkchen/EFRepository/commit/f4f16a9)) 16 | * **repository:** Implement get list feature ([5a6aae0](https://github.com/kirkchen/EFRepository/commit/5a6aae0)) 17 | * **repository:** Implement get list with condition feature ([2df7800](https://github.com/kirkchen/EFRepository/commit/2df7800)) 18 | * **repository:** Implement repository add ([a7d7dcf](https://github.com/kirkchen/EFRepository/commit/a7d7dcf)) 19 | * **repository:** Implement update data feature ([d504caa](https://github.com/kirkchen/EFRepository/commit/d504caa)) 20 | * **repository:** Make repository key generic ([2490cb1](https://github.com/kirkchen/EFRepository/commit/2490cb1)) 21 | * **unitofwork:** Add unitofwork class to support unit of work ([4b3fe72](https://github.com/kirkchen/EFRepository/commit/4b3fe72)) 22 | * **unitofwork:** Remove dbcontext reference when disposing ([3456ca9](https://github.com/kirkchen/EFRepository/commit/3456ca9)) 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /EFRepository.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 | -------------------------------------------------------------------------------- /EFRepository.Tests/EFRepository.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | {BC65819A-550B-4DA1-9AD4-A29EE244AD21} 7 | Library 8 | Properties 9 | EFRepository.Tests 10 | EFRepository.Tests 11 | v4.6.1 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\Castle.Core.3.3.3\lib\net45\Castle.Core.dll 40 | True 41 | 42 | 43 | ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll 44 | True 45 | 46 | 47 | ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll 48 | True 49 | 50 | 51 | ..\packages\Moq.4.5.16\lib\net45\Moq.dll 52 | True 53 | 54 | 55 | 56 | 57 | ..\packages\SpecFlow.2.1.0\lib\net45\TechTalk.SpecFlow.dll 58 | True 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | True 76 | True 77 | GenericRepository.feature 78 | 79 | 80 | 81 | 82 | True 83 | True 84 | SoftDelete.feature 85 | 86 | 87 | 88 | True 89 | True 90 | SystemInfo.feature 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | SpecFlowSingleFileGenerator 105 | GenericRepository.feature.cs 106 | 107 | 108 | 109 | SpecFlowSingleFileGenerator 110 | SoftDelete.feature.cs 111 | 112 | 113 | SpecFlowSingleFileGenerator 114 | SystemInfo.feature.cs 115 | 116 | 117 | 118 | 119 | {3d6fc108-03ab-47e5-96c5-401cb8793bd7} 120 | EFRepository 121 | 122 | 123 | 124 | 125 | 126 | 127 | False 128 | 129 | 130 | False 131 | 132 | 133 | False 134 | 135 | 136 | False 137 | 138 | 139 | 140 | 141 | 142 | 143 | 150 | -------------------------------------------------------------------------------- /EFRepository.Tests/Hooks/SoftDelete.feature: -------------------------------------------------------------------------------- 1 | @Hook 2 | Feature: SoftDelete 3 | 4 | As a programmer
5 | In order to update IsDelete property in data class instead of delete data in the database
6 | I would like to use soft delete hook to handle soft delete logic
7 | 8 | How to use? 9 | -------- 10 | 11 | 1. Create data class inherits **ISoftDelete** 12 | 13 | public class SoftDeleteData : IEntity, ISoftDelete 14 | { 15 | [Key] 16 | public int Id { get; set; } 17 | 18 | public string Content { get; set; } 19 | 20 | public bool IsDelete { get; set; } 21 | } 22 | 23 | 1. Create repository inherits **Generic repository** and register **Soft delete hook** 24 | 25 | public class SoftDeleteRepository : GenericRepository, IRepository 26 | { 27 | public SoftDeleteRepository(MyDbContext context) 28 | : base(context) 29 | { 30 | this.RegisterPostLoadHook(new SoftDeletePostLoadHook()); 31 | this.RegisterPostActionHook(new SoftDeletePostActionHook()); 32 | } 33 | } 34 | 35 | 1. Use repository 36 | 37 | using(var dbContext = new MyDbContext()) 38 | { 39 | var repository = new SoftDeleteRepository(dbContext); 40 | 41 | //// Will update IsDelete to true 42 | repository.Delete(1); 43 | 44 | or 45 | 46 | //// Will only get data with IsDelete=false 47 | var myData = repository.Get(1); 48 | } 49 | 50 | Scenarios 51 | -------- 52 | 53 | Scenario: Get datalist from database should filter IsDelete=true if data is soft delete 54 | Given database has soft delete datas 55 | | Id | Content | IsDelete | 56 | | 1 | TestData | true | 57 | | 2 | TestData 2 | false | 58 | And Register soft delete hook in generic repository 59 | When I use generic repository get data list from database 60 | Then the data list I get should be 61 | | Id | Content | 62 | | 2 | TestData 2 | 63 | 64 | Scenario: Get datalist from database with condition content should contains "2" should filter IsDelete=true if data is soft delete 65 | Given database has soft delete datas 66 | | Id | Content | IsDelete | 67 | | 1 | TestData | true | 68 | | 2 | TestData 2 | false | 69 | | 3 | TestData 2 | true | 70 | And test datas content field should contains "2" 71 | And Register soft delete hook in generic repository 72 | When I use generic repository get data list with condition from database 73 | Then the data list I get should be 74 | | Id | Content | 75 | | 2 | TestData 2 | 76 | 77 | Scenario: Get data from database should filter IsDelete=true if data is soft delete 78 | Given database has soft delete datas 79 | | Id | Content | IsDelete | 80 | | 1 | TestData | true | 81 | | 2 | TestData 2 | false | 82 | And Register soft delete hook in generic repository 83 | When I use generic repository get data from database by id "1" 84 | Then the data list I get should be empty 85 | 86 | Scenario: Get data from database with condition content should contains "2" should filter IsDelete=true if data is soft delete 87 | Given database has soft delete datas 88 | | Id | Content | IsDelete | 89 | | 1 | TestData | true | 90 | | 2 | TestData 2 | true | 91 | | 3 | TestData 2 | false | 92 | And test datas content field should contains "2" 93 | And Register soft delete hook in generic repository 94 | When I use generic repository get data from database with conditon 95 | Then the data list I get should be 96 | | Id | Content | 97 | | 3 | TestData 2 | 98 | 99 | Scenario: Delete data will be replaced by update IsDelete field 100 | Given database has soft delete datas 101 | | Id | Content | IsDelete | 102 | | 1 | TestData | false | 103 | | 2 | TestData 2 | false | 104 | And Register soft delete hook in generic repository 105 | When I use generic repository delete data with id "1" 106 | And I save the changes 107 | Then database should exists test datas 108 | | Id | Content | Is delete | 109 | | 1 | TestData | true | 110 | | 2 | TestData 2 | false | 111 | -------------------------------------------------------------------------------- /EFRepository.Tests/Hooks/SoftDelete.feature.cs: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by SpecFlow (http://www.specflow.org/). 4 | // SpecFlow Version:2.1.0.0 5 | // SpecFlow Generator Version:2.0.0.0 6 | // 7 | // Changes to this file may cause incorrect behavior and will be lost if 8 | // the code is regenerated. 9 | // 10 | // ------------------------------------------------------------------------------ 11 | #region Designer generated code 12 | #pragma warning disable 13 | namespace EFRepository.Tests.Hooks 14 | { 15 | using TechTalk.SpecFlow; 16 | 17 | 18 | [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "2.1.0.0")] 19 | [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 20 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestClassAttribute()] 21 | public partial class SoftDeleteFeature 22 | { 23 | 24 | private static TechTalk.SpecFlow.ITestRunner testRunner; 25 | 26 | #line 1 "SoftDelete.feature" 27 | #line hidden 28 | 29 | [Microsoft.VisualStudio.TestTools.UnitTesting.ClassInitializeAttribute()] 30 | public static void FeatureSetup(Microsoft.VisualStudio.TestTools.UnitTesting.TestContext testContext) 31 | { 32 | testRunner = TechTalk.SpecFlow.TestRunnerManager.GetTestRunner(null, 0); 33 | TechTalk.SpecFlow.FeatureInfo featureInfo = new TechTalk.SpecFlow.FeatureInfo(new System.Globalization.CultureInfo("en-US"), "SoftDelete", @"As a programmer
34 | In order to update IsDelete property in data class instead of delete data in the database
35 | I would like to use soft delete hook to handle soft delete logic
36 | 37 | How to use? 38 | -------- 39 | 40 | 1. Create data class inherits **ISoftDelete** 41 | 42 | public class SoftDeleteData : IEntity, ISoftDelete 43 | { 44 | [Key] 45 | public int Id { get; set; } 46 | 47 | public string Content { get; set; } 48 | 49 | public bool IsDelete { get; set; } 50 | } 51 | 52 | 1. Create repository inherits **Generic repository** and register **Soft delete hook** 53 | 54 | public class SoftDeleteRepository : GenericRepository, IRepository 55 | { 56 | public SoftDeleteRepository(MyDbContext context) 57 | : base(context) 58 | { 59 | this.RegisterPostLoadHook(new SoftDeletePostLoadHook()); 60 | this.RegisterPostActionHook(new SoftDeletePostActionHook()); 61 | } 62 | } 63 | 64 | 1. Use repository 65 | 66 | using(var dbContext = new MyDbContext()) 67 | { 68 | var repository = new SoftDeleteRepository(dbContext); 69 | 70 | //// Will update IsDelete to true 71 | repository.Delete(1); 72 | 73 | or 74 | 75 | //// Will only get data with IsDelete=false 76 | var myData = repository.Get(1); 77 | } 78 | 79 | Scenarios 80 | --------", ProgrammingLanguage.CSharp, new string[] { 81 | "Hook"}); 82 | testRunner.OnFeatureStart(featureInfo); 83 | } 84 | 85 | [Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupAttribute()] 86 | public static void FeatureTearDown() 87 | { 88 | testRunner.OnFeatureEnd(); 89 | testRunner = null; 90 | } 91 | 92 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestInitializeAttribute()] 93 | public virtual void TestInitialize() 94 | { 95 | if (((testRunner.FeatureContext != null) 96 | && (testRunner.FeatureContext.FeatureInfo.Title != "SoftDelete"))) 97 | { 98 | EFRepository.Tests.Hooks.SoftDeleteFeature.FeatureSetup(null); 99 | } 100 | } 101 | 102 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestCleanupAttribute()] 103 | public virtual void ScenarioTearDown() 104 | { 105 | testRunner.OnScenarioEnd(); 106 | } 107 | 108 | public virtual void ScenarioSetup(TechTalk.SpecFlow.ScenarioInfo scenarioInfo) 109 | { 110 | testRunner.OnScenarioStart(scenarioInfo); 111 | } 112 | 113 | public virtual void ScenarioCleanup() 114 | { 115 | testRunner.CollectScenarioErrors(); 116 | } 117 | 118 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()] 119 | [Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Get datalist from database should filter IsDelete=true if data is soft delete")] 120 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "SoftDelete")] 121 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestCategoryAttribute("Hook")] 122 | public virtual void GetDatalistFromDatabaseShouldFilterIsDeleteTrueIfDataIsSoftDelete() 123 | { 124 | TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Get datalist from database should filter IsDelete=true if data is soft delete", ((string[])(null))); 125 | #line 53 126 | this.ScenarioSetup(scenarioInfo); 127 | #line hidden 128 | TechTalk.SpecFlow.Table table1 = new TechTalk.SpecFlow.Table(new string[] { 129 | "Id", 130 | "Content", 131 | "IsDelete"}); 132 | table1.AddRow(new string[] { 133 | "1", 134 | "TestData", 135 | "true"}); 136 | table1.AddRow(new string[] { 137 | "2", 138 | "TestData 2", 139 | "false"}); 140 | #line 54 141 | testRunner.Given("database has soft delete datas", ((string)(null)), table1, "Given "); 142 | #line 58 143 | testRunner.And("Register soft delete hook in generic repository", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); 144 | #line 59 145 | testRunner.When("I use generic repository get data list from database", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); 146 | #line hidden 147 | TechTalk.SpecFlow.Table table2 = new TechTalk.SpecFlow.Table(new string[] { 148 | "Id", 149 | "Content"}); 150 | table2.AddRow(new string[] { 151 | "2", 152 | "TestData 2"}); 153 | #line 60 154 | testRunner.Then("the data list I get should be", ((string)(null)), table2, "Then "); 155 | #line hidden 156 | this.ScenarioCleanup(); 157 | } 158 | 159 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()] 160 | [Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Get datalist from database with condition content should contains \"2\" should filt" + 161 | "er IsDelete=true if data is soft delete")] 162 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "SoftDelete")] 163 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestCategoryAttribute("Hook")] 164 | public virtual void GetDatalistFromDatabaseWithConditionContentShouldContains2ShouldFilterIsDeleteTrueIfDataIsSoftDelete() 165 | { 166 | TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Get datalist from database with condition content should contains \"2\" should filt" + 167 | "er IsDelete=true if data is soft delete", ((string[])(null))); 168 | #line 64 169 | this.ScenarioSetup(scenarioInfo); 170 | #line hidden 171 | TechTalk.SpecFlow.Table table3 = new TechTalk.SpecFlow.Table(new string[] { 172 | "Id", 173 | "Content", 174 | "IsDelete"}); 175 | table3.AddRow(new string[] { 176 | "1", 177 | "TestData", 178 | "true"}); 179 | table3.AddRow(new string[] { 180 | "2", 181 | "TestData 2", 182 | "false"}); 183 | table3.AddRow(new string[] { 184 | "3", 185 | "TestData 2", 186 | "true"}); 187 | #line 65 188 | testRunner.Given("database has soft delete datas", ((string)(null)), table3, "Given "); 189 | #line 70 190 | testRunner.And("test datas content field should contains \"2\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); 191 | #line 71 192 | testRunner.And("Register soft delete hook in generic repository", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); 193 | #line 72 194 | testRunner.When("I use generic repository get data list with condition from database", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); 195 | #line hidden 196 | TechTalk.SpecFlow.Table table4 = new TechTalk.SpecFlow.Table(new string[] { 197 | "Id", 198 | "Content"}); 199 | table4.AddRow(new string[] { 200 | "2", 201 | "TestData 2"}); 202 | #line 73 203 | testRunner.Then("the data list I get should be", ((string)(null)), table4, "Then "); 204 | #line hidden 205 | this.ScenarioCleanup(); 206 | } 207 | 208 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()] 209 | [Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Get data from database should filter IsDelete=true if data is soft delete")] 210 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "SoftDelete")] 211 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestCategoryAttribute("Hook")] 212 | public virtual void GetDataFromDatabaseShouldFilterIsDeleteTrueIfDataIsSoftDelete() 213 | { 214 | TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Get data from database should filter IsDelete=true if data is soft delete", ((string[])(null))); 215 | #line 77 216 | this.ScenarioSetup(scenarioInfo); 217 | #line hidden 218 | TechTalk.SpecFlow.Table table5 = new TechTalk.SpecFlow.Table(new string[] { 219 | "Id", 220 | "Content", 221 | "IsDelete"}); 222 | table5.AddRow(new string[] { 223 | "1", 224 | "TestData", 225 | "true"}); 226 | table5.AddRow(new string[] { 227 | "2", 228 | "TestData 2", 229 | "false"}); 230 | #line 78 231 | testRunner.Given("database has soft delete datas", ((string)(null)), table5, "Given "); 232 | #line 82 233 | testRunner.And("Register soft delete hook in generic repository", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); 234 | #line 83 235 | testRunner.When("I use generic repository get data from database by id \"1\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); 236 | #line 84 237 | testRunner.Then("the data list I get should be empty", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then "); 238 | #line hidden 239 | this.ScenarioCleanup(); 240 | } 241 | 242 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()] 243 | [Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Get data from database with condition content should contains \"2\" should filter I" + 244 | "sDelete=true if data is soft delete")] 245 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "SoftDelete")] 246 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestCategoryAttribute("Hook")] 247 | public virtual void GetDataFromDatabaseWithConditionContentShouldContains2ShouldFilterIsDeleteTrueIfDataIsSoftDelete() 248 | { 249 | TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Get data from database with condition content should contains \"2\" should filter I" + 250 | "sDelete=true if data is soft delete", ((string[])(null))); 251 | #line 86 252 | this.ScenarioSetup(scenarioInfo); 253 | #line hidden 254 | TechTalk.SpecFlow.Table table6 = new TechTalk.SpecFlow.Table(new string[] { 255 | "Id", 256 | "Content", 257 | "IsDelete"}); 258 | table6.AddRow(new string[] { 259 | "1", 260 | "TestData", 261 | "true"}); 262 | table6.AddRow(new string[] { 263 | "2", 264 | "TestData 2", 265 | "true"}); 266 | table6.AddRow(new string[] { 267 | "3", 268 | "TestData 2", 269 | "false"}); 270 | #line 87 271 | testRunner.Given("database has soft delete datas", ((string)(null)), table6, "Given "); 272 | #line 92 273 | testRunner.And("test datas content field should contains \"2\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); 274 | #line 93 275 | testRunner.And("Register soft delete hook in generic repository", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); 276 | #line 94 277 | testRunner.When("I use generic repository get data from database with conditon", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); 278 | #line hidden 279 | TechTalk.SpecFlow.Table table7 = new TechTalk.SpecFlow.Table(new string[] { 280 | "Id", 281 | "Content"}); 282 | table7.AddRow(new string[] { 283 | "3", 284 | "TestData 2"}); 285 | #line 95 286 | testRunner.Then("the data list I get should be", ((string)(null)), table7, "Then "); 287 | #line hidden 288 | this.ScenarioCleanup(); 289 | } 290 | 291 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()] 292 | [Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Delete data will be replaced by update IsDelete field")] 293 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "SoftDelete")] 294 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestCategoryAttribute("Hook")] 295 | public virtual void DeleteDataWillBeReplacedByUpdateIsDeleteField() 296 | { 297 | TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Delete data will be replaced by update IsDelete field", ((string[])(null))); 298 | #line 99 299 | this.ScenarioSetup(scenarioInfo); 300 | #line hidden 301 | TechTalk.SpecFlow.Table table8 = new TechTalk.SpecFlow.Table(new string[] { 302 | "Id", 303 | "Content", 304 | "IsDelete"}); 305 | table8.AddRow(new string[] { 306 | "1", 307 | "TestData", 308 | "false"}); 309 | table8.AddRow(new string[] { 310 | "2", 311 | "TestData 2", 312 | "false"}); 313 | #line 100 314 | testRunner.Given("database has soft delete datas", ((string)(null)), table8, "Given "); 315 | #line 104 316 | testRunner.And("Register soft delete hook in generic repository", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); 317 | #line 105 318 | testRunner.When("I use generic repository delete data with id \"1\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); 319 | #line 106 320 | testRunner.And("I save the changes", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); 321 | #line hidden 322 | TechTalk.SpecFlow.Table table9 = new TechTalk.SpecFlow.Table(new string[] { 323 | "Id", 324 | "Content", 325 | "Is delete"}); 326 | table9.AddRow(new string[] { 327 | "1", 328 | "TestData", 329 | "true"}); 330 | table9.AddRow(new string[] { 331 | "2", 332 | "TestData 2", 333 | "false"}); 334 | #line 107 335 | testRunner.Then("database should exists test datas", ((string)(null)), table9, "Then "); 336 | #line hidden 337 | this.ScenarioCleanup(); 338 | } 339 | } 340 | } 341 | #pragma warning restore 342 | #endregion 343 | -------------------------------------------------------------------------------- /EFRepository.Tests/Hooks/SoftDeleteSteps.cs: -------------------------------------------------------------------------------- 1 | using EFRepository.Hooks; 2 | using EFRepository.Tests.TestClasses; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Linq.Expressions; 8 | using TechTalk.SpecFlow; 9 | using TechTalk.SpecFlow.Assist; 10 | 11 | namespace EFRepository.Tests.Hooks 12 | { 13 | [Binding] 14 | [Scope(Feature = "SoftDelete")] 15 | public class SoftDeleteSteps 16 | { 17 | public string ConnectionString { get; private set; } 18 | 19 | public TestDbContext DbContext { get; private set; } 20 | 21 | public SoftDeleteRepository Repository { get; private set; } 22 | 23 | public Expression> Condition { get; set; } 24 | 25 | public SoftDeleteData DataItem { get; private set; } 26 | 27 | public IEnumerable DataList { get; private set; } 28 | 29 | [BeforeScenario] 30 | public void BeforeScenario() 31 | { 32 | this.ConnectionString = Environment.GetEnvironmentVariable("TestDb") ?? "TestDb"; 33 | 34 | this.DbContext = new TestDbContext(ConnectionString); 35 | this.DbContext.Database.Delete(); 36 | 37 | this.Repository = new SoftDeleteRepository(this.DbContext); 38 | } 39 | 40 | [AfterScenario] 41 | public void AfterScenario() 42 | { 43 | this.DbContext.Dispose(); 44 | } 45 | 46 | [Given(@"database has soft delete datas")] 47 | public void GivenDatabaseHasSoftDeleteDatas(Table table) 48 | { 49 | var dataList = table.CreateSet(); 50 | 51 | using (var dbContext = new TestDbContext(this.ConnectionString)) 52 | { 53 | dbContext.SoftDeleteDatas.AddRange(dataList); 54 | dbContext.SaveChanges(); 55 | } 56 | } 57 | 58 | [Given(@"Register soft delete hook in generic repository")] 59 | public void GivenRegisterSoftDeleteHookInGenericRepository() 60 | { 61 | this.Repository.RegisterPostLoadHook(new SoftDeletePostLoadHook()); 62 | this.Repository.RegisterPostActionHook(new SoftDeletePostActionHook()); 63 | } 64 | 65 | [Given(@"test datas content field should contains ""(.*)""")] 66 | public void GivenTestDatasContentFieldShouldContains(string condition) 67 | { 68 | this.Condition = (data) => data.Content.Contains(condition); 69 | } 70 | 71 | [When(@"I use generic repository delete data with id ""(.*)""")] 72 | public void WhenIUseGenericRepositoryDeleteDataWithId(int id) 73 | { 74 | this.Repository.Delete(id); 75 | } 76 | 77 | [When(@"I use generic repository get data list from database")] 78 | public void WhenIUseGenericRepositoryGetDataListFromDatabase() 79 | { 80 | this.DataList = this.Repository.GetList(); 81 | } 82 | 83 | [When(@"I use generic repository get data list with condition from database")] 84 | public void WhenIUseGenericRepositoryGetDataListWithConditionFromDatabase() 85 | { 86 | this.DataList = this.Repository.GetList(this.Condition); 87 | } 88 | 89 | [When(@"I use generic repository get data from database by id ""(.*)""")] 90 | public void WhenIUseGenericRepositoryGetDataFromDatabaseById(int id) 91 | { 92 | this.DataItem = this.Repository.Get(id); 93 | 94 | if (this.DataItem != null) 95 | { 96 | this.DataList = new List() { this.DataItem }; 97 | } 98 | } 99 | 100 | [When(@"I use generic repository get data from database with conditon")] 101 | public void WhenIUseGenericRepositoryGetDataFromDatabaseWithConditon() 102 | { 103 | this.DataItem = this.Repository.Get(this.Condition); 104 | 105 | if (this.DataItem != null) 106 | { 107 | this.DataList = new List() { this.DataItem }; 108 | } 109 | } 110 | 111 | 112 | [When(@"I save the changes")] 113 | public void WhenISaveTheChanges() 114 | { 115 | this.Repository.SaveChanges(); 116 | } 117 | 118 | [Then(@"database should exists test datas")] 119 | public void ThenDatabaseShouldExistsTestDatas(Table table) 120 | { 121 | using (var dbContext = new TestDbContext(this.ConnectionString)) 122 | { 123 | var datalist = dbContext.SoftDeleteDatas.ToList(); 124 | 125 | table.CompareToSet(datalist); 126 | } 127 | } 128 | 129 | [Then(@"the data list I get should be")] 130 | public void ThenTheDataListIGetShouldBe(Table table) 131 | { 132 | table.CompareToSet(this.DataList); 133 | } 134 | 135 | [Then(@"the data list I get should be empty")] 136 | public void ThenTheDataListIGetShouldBeEmpty() 137 | { 138 | Assert.IsNull(this.DataList); 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /EFRepository.Tests/Hooks/SystemInfo.feature: -------------------------------------------------------------------------------- 1 | @Hook 2 | Feature: SystemInfo 3 | 4 | As a programmer
5 | In order to auto assign required system information when insert or update data
6 | I would like to use use system info hook to handle assign system infomation logic
7 | 8 | How to use? 9 | -------- 10 | 11 | 1. Create data class inherits **ISystemInfo** 12 | 13 | public class SystemInfoData : IEntity, ISystemInfo 14 | { 15 | [Key] 16 | public int Id { get; set; } 17 | 18 | public string Content { get; set; } 19 | 20 | public DateTime CreatedAt { get; set; } 21 | 22 | public string CreatedBy { get; set; } 23 | 24 | public DateTime UpdatedAt { get; set; } 25 | 26 | public string UpdatedBy { get; set; } 27 | } 28 | 29 | 1. Create **UserHelper** class to get current username in your system 30 | 31 | public class UserHelper: IUserHelper 32 | { 33 | public string GetUserName() 34 | { 35 | //// Implement your system user name logic 36 | return HttpContext.Current.User.Name; 37 | } 38 | } 39 | 40 | 1. Create repository inherits **Generic repository** and register **System info hook** 41 | 42 | public class SystemInfoRepository : GenericRepository, IRepository 43 | { 44 | public SystemInfoRepository(MyDbContext context) 45 | : base(context) 46 | { 47 | this.Repository.RegisterPostActionHook(new SystemInfoPostActionHook(new UserHelper(), new DatetimeHelper())); 48 | } 49 | } 50 | 51 | 1. Use repository 52 | 53 | using(var dbContext = new MyDbContext()) 54 | { 55 | var repository = new SoftDeleteRepository(dbContext); 56 | 57 | //// Will auto update required system info field 58 | repository.Add(myData); 59 | 60 | or 61 | 62 | //// Will auto update required system info field 63 | repository.Update(myData); 64 | } 65 | 66 | Scenarios 67 | -------- 68 | 69 | Scenario: Add data into database should be success and auto assign system required infomation 70 | Given I have systemInfo datas 71 | | Id | Content | 72 | | 1 | TestData | 73 | And Current user is "John" 74 | And Current datetime is "2016/08/05 16:00:00" 75 | And Register system info hook in generic repository 76 | When I use generic repository to add data 77 | And I save the changes 78 | Then database should exists test datas 79 | | Id | Content | CreatedAt | CreatedBy | UpdatedAt | UpdatedBy | 80 | | 1 | TestData | 2016/08/05 16:00:00 | John | 2016/08/05 16:00:00 | John | 81 | 82 | Scenario: Add datalist into database should be success and auto assign system required infomation 83 | Given I have systemInfo datas 84 | | Id | Content | 85 | | 1 | TestData | 86 | | 2 | TestData 2 | 87 | And Current user is "John" 88 | And Current datetime is "2016/08/05 16:00:00" 89 | And Register system info hook in generic repository 90 | When I use generic repository to add datalist 91 | And I save the changes 92 | Then database should exists test datas 93 | | Id | Content | CreatedAt | CreatedBy | UpdatedAt | UpdatedBy | 94 | | 1 | TestData | 2016/08/05 16:00:00 | John | 2016/08/05 16:00:00 | John | 95 | | 2 | TestData 2 | 2016/08/05 16:00:00 | John | 2016/08/05 16:00:00 | John | 96 | 97 | Scenario: Update data which is exists in database should be success and auto assign system required infomation 98 | Given database has systemInfo datas 99 | | Id | Content | CreatedAt | CreatedBy | UpdatedAt | UpdatedBy | 100 | | 1 | TestData | 2016/08/05 16:00:00 | John | 2016/08/05 16:00:00 | John | 101 | | 2 | TestData 2 | 2016/08/05 16:00:00 | John | 2016/08/05 16:00:00 | John | 102 | And the data I want to update is 103 | | Id | Content | CreatedAt | CreatedBy | UpdatedAt | UpdatedBy | 104 | | 1 | TestData Modified | 2016/08/05 16:00:00 | John | 2016/08/05 16:00:00 | John | 105 | And Current user is "David" 106 | And Current datetime is "2016/08/06 09:00:00" 107 | And Register system info hook in generic repository 108 | When I use generic repository update data 109 | And I save the changes 110 | Then database should exists test datas 111 | | Id | Content | CreatedAt | CreatedBy | UpdatedAt | UpdatedBy | 112 | | 1 | TestData Modified | 2016/08/05 16:00:00 | John | 2016/08/06 09:00:00 | David | 113 | | 2 | TestData 2 | 2016/08/05 16:00:00 | John | 2016/08/05 16:00:00 | John | 114 | -------------------------------------------------------------------------------- /EFRepository.Tests/Hooks/SystemInfo.feature.cs: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by SpecFlow (http://www.specflow.org/). 4 | // SpecFlow Version:2.1.0.0 5 | // SpecFlow Generator Version:2.0.0.0 6 | // 7 | // Changes to this file may cause incorrect behavior and will be lost if 8 | // the code is regenerated. 9 | // 10 | // ------------------------------------------------------------------------------ 11 | #region Designer generated code 12 | #pragma warning disable 13 | namespace EFRepository.Tests.Hooks 14 | { 15 | using TechTalk.SpecFlow; 16 | 17 | 18 | [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "2.1.0.0")] 19 | [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 20 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestClassAttribute()] 21 | public partial class SystemInfoFeature 22 | { 23 | 24 | private static TechTalk.SpecFlow.ITestRunner testRunner; 25 | 26 | #line 1 "SystemInfo.feature" 27 | #line hidden 28 | 29 | [Microsoft.VisualStudio.TestTools.UnitTesting.ClassInitializeAttribute()] 30 | public static void FeatureSetup(Microsoft.VisualStudio.TestTools.UnitTesting.TestContext testContext) 31 | { 32 | testRunner = TechTalk.SpecFlow.TestRunnerManager.GetTestRunner(null, 0); 33 | TechTalk.SpecFlow.FeatureInfo featureInfo = new TechTalk.SpecFlow.FeatureInfo(new System.Globalization.CultureInfo("en-US"), "SystemInfo", "As a programmer
\r\nIn order to auto assign required system information when " + 34 | "insert or update data
\r\nI would like to use use system info hook to handle" + 35 | " assign system infomation logic
\r\n\r\nHow to use?\r\n--------\r\n\r\n1. Create dat" + 36 | "a class inherits **ISystemInfo**\r\n\t\t\r\n\t\tpublic class SystemInfoData : IEntity, ISystemInfo\r\n\t\t{ \r\n\t\t\t[Key]\r\n\t\t\tpublic int Id { get; set; }\r\n \r" + 38 | "\n\t\t\tpublic string Content { get; set; }\r\n \r\n\t\t\tpublic DateTime CreatedAt { " + 39 | "get; set; }\r\n\r\n\t public string CreatedBy { get; set; }\r\n\r\n\t\t\tpublic DateT" + 40 | "ime UpdatedAt { get; set; }\r\n\r\n\t\t\tpublic string UpdatedBy { get; set; }\r\n\t\t}\t\r\n\r" + 41 | "\n1. Create **UserHelper** class to get current username in your system\r\n\r\n\t\tpubl" + 42 | "ic class UserHelper: IUserHelper\r\n\t\t{\r\n\t\t\tpublic string GetUserName()\r\n\t\t\t{\r\n\t\t\t" + 43 | "\t//// Implement your system user name logic\r\n\t\t\t\treturn HttpContext.Current.User" + 44 | ".Name;\r\n\t\t\t}\r\n\t\t}\r\n\r\n1. Create repository inherits **Generic repository** and re" + 45 | "gister **System info hook**\r\n\r\n\t\tpublic class SystemInfoRepository : GenericRepo" + 46 | "sitory, IRepository\r\n\t\t{ \r\n\t\t\tp" + 47 | "ublic SystemInfoRepository(MyDbContext context)\r\n\t\t\t\t: base(context)\r\n\t\t\t{\r\n\t\t\t\t" + 48 | "this.Repository.RegisterPostActionHook(new SystemInfoPostActionHook(new UserHelper(), new DatetimeHelper()));\r\n\t\t\t}\r\n\t\t}\r\n\r\n1. Use repository\r\n\r" + 50 | "\n\t\tusing(var dbContext = new MyDbContext())\r\n\t\t{\r\n\t\t\tvar repository = new SoftDe" + 51 | "leteRepository(dbContext);\r\n\r\n\t\t\t//// Will auto update required system info fiel" + 52 | "d\r\n\t\t\trepository.Add(myData);\r\n\r\n\t\t\tor\r\n\r\n\t\t\t//// Will auto update required syst" + 53 | "em info field\r\n\t\t\trepository.Update(myData);\r\n\t\t}\r\n\r\nScenarios\r\n--------", ProgrammingLanguage.CSharp, new string[] { 54 | "Hook"}); 55 | testRunner.OnFeatureStart(featureInfo); 56 | } 57 | 58 | [Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupAttribute()] 59 | public static void FeatureTearDown() 60 | { 61 | testRunner.OnFeatureEnd(); 62 | testRunner = null; 63 | } 64 | 65 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestInitializeAttribute()] 66 | public virtual void TestInitialize() 67 | { 68 | if (((testRunner.FeatureContext != null) 69 | && (testRunner.FeatureContext.FeatureInfo.Title != "SystemInfo"))) 70 | { 71 | EFRepository.Tests.Hooks.SystemInfoFeature.FeatureSetup(null); 72 | } 73 | } 74 | 75 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestCleanupAttribute()] 76 | public virtual void ScenarioTearDown() 77 | { 78 | testRunner.OnScenarioEnd(); 79 | } 80 | 81 | public virtual void ScenarioSetup(TechTalk.SpecFlow.ScenarioInfo scenarioInfo) 82 | { 83 | testRunner.OnScenarioStart(scenarioInfo); 84 | } 85 | 86 | public virtual void ScenarioCleanup() 87 | { 88 | testRunner.CollectScenarioErrors(); 89 | } 90 | 91 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()] 92 | [Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Add data into database should be success and auto assign system required infomati" + 93 | "on")] 94 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "SystemInfo")] 95 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestCategoryAttribute("Hook")] 96 | public virtual void AddDataIntoDatabaseShouldBeSuccessAndAutoAssignSystemRequiredInfomation() 97 | { 98 | TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Add data into database should be success and auto assign system required infomati" + 99 | "on", ((string[])(null))); 100 | #line 69 101 | this.ScenarioSetup(scenarioInfo); 102 | #line hidden 103 | TechTalk.SpecFlow.Table table1 = new TechTalk.SpecFlow.Table(new string[] { 104 | "Id", 105 | "Content"}); 106 | table1.AddRow(new string[] { 107 | "1", 108 | "TestData"}); 109 | #line 70 110 | testRunner.Given("I have systemInfo datas", ((string)(null)), table1, "Given "); 111 | #line 73 112 | testRunner.And("Current user is \"John\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); 113 | #line 74 114 | testRunner.And("Current datetime is \"2016/08/05 16:00:00\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); 115 | #line 75 116 | testRunner.And("Register system info hook in generic repository", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); 117 | #line 76 118 | testRunner.When("I use generic repository to add data", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); 119 | #line 77 120 | testRunner.And("I save the changes", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); 121 | #line hidden 122 | TechTalk.SpecFlow.Table table2 = new TechTalk.SpecFlow.Table(new string[] { 123 | "Id", 124 | "Content", 125 | "CreatedAt", 126 | "CreatedBy", 127 | "UpdatedAt", 128 | "UpdatedBy"}); 129 | table2.AddRow(new string[] { 130 | "1", 131 | "TestData", 132 | "2016/08/05 16:00:00", 133 | "John", 134 | "2016/08/05 16:00:00", 135 | "John"}); 136 | #line 78 137 | testRunner.Then("database should exists test datas", ((string)(null)), table2, "Then "); 138 | #line hidden 139 | this.ScenarioCleanup(); 140 | } 141 | 142 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()] 143 | [Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Add datalist into database should be success and auto assign system required info" + 144 | "mation")] 145 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "SystemInfo")] 146 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestCategoryAttribute("Hook")] 147 | public virtual void AddDatalistIntoDatabaseShouldBeSuccessAndAutoAssignSystemRequiredInfomation() 148 | { 149 | TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Add datalist into database should be success and auto assign system required info" + 150 | "mation", ((string[])(null))); 151 | #line 82 152 | this.ScenarioSetup(scenarioInfo); 153 | #line hidden 154 | TechTalk.SpecFlow.Table table3 = new TechTalk.SpecFlow.Table(new string[] { 155 | "Id", 156 | "Content"}); 157 | table3.AddRow(new string[] { 158 | "1", 159 | "TestData"}); 160 | table3.AddRow(new string[] { 161 | "2", 162 | "TestData 2"}); 163 | #line 83 164 | testRunner.Given("I have systemInfo datas", ((string)(null)), table3, "Given "); 165 | #line 87 166 | testRunner.And("Current user is \"John\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); 167 | #line 88 168 | testRunner.And("Current datetime is \"2016/08/05 16:00:00\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); 169 | #line 89 170 | testRunner.And("Register system info hook in generic repository", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); 171 | #line 90 172 | testRunner.When("I use generic repository to add datalist", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); 173 | #line 91 174 | testRunner.And("I save the changes", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); 175 | #line hidden 176 | TechTalk.SpecFlow.Table table4 = new TechTalk.SpecFlow.Table(new string[] { 177 | "Id", 178 | "Content", 179 | "CreatedAt", 180 | "CreatedBy", 181 | "UpdatedAt", 182 | "UpdatedBy"}); 183 | table4.AddRow(new string[] { 184 | "1", 185 | "TestData", 186 | "2016/08/05 16:00:00", 187 | "John", 188 | "2016/08/05 16:00:00", 189 | "John"}); 190 | table4.AddRow(new string[] { 191 | "2", 192 | "TestData 2", 193 | "2016/08/05 16:00:00", 194 | "John", 195 | "2016/08/05 16:00:00", 196 | "John"}); 197 | #line 92 198 | testRunner.Then("database should exists test datas", ((string)(null)), table4, "Then "); 199 | #line hidden 200 | this.ScenarioCleanup(); 201 | } 202 | 203 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()] 204 | [Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Update data which is exists in database should be success and auto assign system " + 205 | "required infomation")] 206 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "SystemInfo")] 207 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestCategoryAttribute("Hook")] 208 | public virtual void UpdateDataWhichIsExistsInDatabaseShouldBeSuccessAndAutoAssignSystemRequiredInfomation() 209 | { 210 | TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Update data which is exists in database should be success and auto assign system " + 211 | "required infomation", ((string[])(null))); 212 | #line 97 213 | this.ScenarioSetup(scenarioInfo); 214 | #line hidden 215 | TechTalk.SpecFlow.Table table5 = new TechTalk.SpecFlow.Table(new string[] { 216 | "Id", 217 | "Content", 218 | "CreatedAt", 219 | "CreatedBy", 220 | "UpdatedAt", 221 | "UpdatedBy"}); 222 | table5.AddRow(new string[] { 223 | "1", 224 | "TestData", 225 | "2016/08/05 16:00:00", 226 | "John", 227 | "2016/08/05 16:00:00", 228 | "John"}); 229 | table5.AddRow(new string[] { 230 | "2", 231 | "TestData 2", 232 | "2016/08/05 16:00:00", 233 | "John", 234 | "2016/08/05 16:00:00", 235 | "John"}); 236 | #line 98 237 | testRunner.Given("database has systemInfo datas", ((string)(null)), table5, "Given "); 238 | #line hidden 239 | TechTalk.SpecFlow.Table table6 = new TechTalk.SpecFlow.Table(new string[] { 240 | "Id", 241 | "Content", 242 | "CreatedAt", 243 | "CreatedBy", 244 | "UpdatedAt", 245 | "UpdatedBy"}); 246 | table6.AddRow(new string[] { 247 | "1", 248 | "TestData Modified", 249 | "2016/08/05 16:00:00", 250 | "John", 251 | "2016/08/05 16:00:00", 252 | "John"}); 253 | #line 102 254 | testRunner.And("the data I want to update is", ((string)(null)), table6, "And "); 255 | #line 105 256 | testRunner.And("Current user is \"David\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); 257 | #line 106 258 | testRunner.And("Current datetime is \"2016/08/06 09:00:00\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); 259 | #line 107 260 | testRunner.And("Register system info hook in generic repository", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); 261 | #line 108 262 | testRunner.When("I use generic repository update data", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); 263 | #line 109 264 | testRunner.And("I save the changes", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); 265 | #line hidden 266 | TechTalk.SpecFlow.Table table7 = new TechTalk.SpecFlow.Table(new string[] { 267 | "Id", 268 | "Content", 269 | "CreatedAt", 270 | "CreatedBy", 271 | "UpdatedAt", 272 | "UpdatedBy"}); 273 | table7.AddRow(new string[] { 274 | "1", 275 | "TestData Modified", 276 | "2016/08/05 16:00:00", 277 | "John", 278 | "2016/08/06 09:00:00", 279 | "David"}); 280 | table7.AddRow(new string[] { 281 | "2", 282 | "TestData 2", 283 | "2016/08/05 16:00:00", 284 | "John", 285 | "2016/08/05 16:00:00", 286 | "John"}); 287 | #line 110 288 | testRunner.Then("database should exists test datas", ((string)(null)), table7, "Then "); 289 | #line hidden 290 | this.ScenarioCleanup(); 291 | } 292 | } 293 | } 294 | #pragma warning restore 295 | #endregion 296 | -------------------------------------------------------------------------------- /EFRepository.Tests/Hooks/SystemInfoSteps.cs: -------------------------------------------------------------------------------- 1 | using EFRepository.Hooks; 2 | using EFRepository.Tests.TestClasses; 3 | using Moq; 4 | using Moq.Protected; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using TechTalk.SpecFlow; 9 | using TechTalk.SpecFlow.Assist; 10 | 11 | namespace EFRepository.Tests.Hooks 12 | { 13 | [Binding] 14 | [Scope(Feature = "SystemInfo")] 15 | public class SystemInfoSteps 16 | { 17 | public string ConnectionString { get; private set; } 18 | 19 | public TestDbContext DbContext { get; private set; } 20 | 21 | public SystemInfoRepository Repository { get; private set; } 22 | 23 | public SystemInfoData DataItem { get; private set; } 24 | 25 | public IEnumerable DataList { get; private set; } 26 | 27 | public Mock UserHelperMock { get; private set; } 28 | 29 | public Mock DatetimeHelperMock { get; private set; } 30 | 31 | [BeforeScenario] 32 | public void BeforeScenario() 33 | { 34 | this.ConnectionString = Environment.GetEnvironmentVariable("TestDb") ?? "TestDb"; 35 | 36 | this.DbContext = new TestDbContext(ConnectionString); 37 | this.DbContext.Database.Delete(); 38 | 39 | this.Repository = new SystemInfoRepository(this.DbContext); 40 | 41 | this.UserHelperMock = new Mock(); 42 | this.DatetimeHelperMock = new Mock(); 43 | } 44 | 45 | [AfterScenario] 46 | public void AfterScenario() 47 | { 48 | this.DbContext.Dispose(); 49 | } 50 | 51 | [Given(@"I have systemInfo datas")] 52 | public void GivenIHaveSystemInfoDatas(Table table) 53 | { 54 | this.DataList = table.CreateSet(); 55 | } 56 | 57 | [Given(@"database has systemInfo datas")] 58 | public void GivenDatabaseHasSystemInfoDatas(Table table) 59 | { 60 | var dataList = table.CreateSet(); 61 | 62 | using (var dbContext = new TestDbContext(this.ConnectionString)) 63 | { 64 | dbContext.SystemInfoDatas.AddRange(dataList); 65 | dbContext.SaveChanges(); 66 | } 67 | } 68 | 69 | [Given(@"the data I want to update is")] 70 | public void GivenTheDataIWantToUpdateIs(Table table) 71 | { 72 | this.DataItem = table.CreateInstance(); 73 | } 74 | 75 | [Given(@"Current user is ""(.*)""")] 76 | public void GivenCurrentUserIs(string userName) 77 | { 78 | this.UserHelperMock.Setup(i => i.GetUserName()).Returns(userName); 79 | } 80 | 81 | [Given(@"Current datetime is ""(.*)""")] 82 | public void GivenCurrentDatetimeIs(DateTime currentDatetime) 83 | { 84 | this.DatetimeHelperMock.Setup(i => i.GetCurrentTime()).Returns(currentDatetime); 85 | } 86 | 87 | [Given(@"Register system info hook in generic repository")] 88 | public void GivenRegisterSystemInfoHookInGenericRepository() 89 | { 90 | this.Repository.RegisterPostActionHook(new SystemInfoPostActionHook(this.UserHelperMock.Object, this.DatetimeHelperMock.Object)); 91 | } 92 | 93 | [When(@"I use generic repository to add data")] 94 | public void WhenIUseGenericRepositoryToAddData() 95 | { 96 | this.Repository.Add(this.DataList.First()); 97 | } 98 | 99 | [When(@"I use generic repository to add datalist")] 100 | public void WhenIUseGenericRepositoryToAddDatalist() 101 | { 102 | this.Repository.AddRange(this.DataList); 103 | } 104 | 105 | [When(@"I use generic repository update data")] 106 | public void WhenIUseGenericRepositoryUpdateData() 107 | { 108 | this.Repository.Update(this.DataItem); 109 | } 110 | 111 | [When(@"I save the changes")] 112 | public void WhenISaveTheChanges() 113 | { 114 | this.Repository.SaveChanges(); 115 | } 116 | 117 | [Then(@"database should exists test datas")] 118 | public void ThenDatabaseShouldExistsTestDatas(Table table) 119 | { 120 | using (var dbContext = new TestDbContext(this.ConnectionString)) 121 | { 122 | var datalist = dbContext.SystemInfoDatas.ToList(); 123 | 124 | table.CompareToSet(datalist); 125 | } 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /EFRepository.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("EFRepository.Tests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("EFRepository.Tests")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("bc65819a-550b-4da1-9ad4-a29ee244ad21")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /EFRepository.Tests/Repositories/GenericRepository.feature: -------------------------------------------------------------------------------- 1 | @Repository 2 | Feature: GenericRepository 3 | 4 | As a programmer
5 | In order to reduce the work of writing repository
6 | I would like to create a GenericRepository to process database related logic
7 | 8 | How to use? 9 | -------- 10 | 11 | 1. Create data class inherits **IEntity** 12 | 13 | public class TestData : IEntity 14 | { 15 | [Key] 16 | public int Id { get; set; } 17 | 18 | public string Content { get; set; } 19 | } 20 | 21 | 1. Create repository inherits **Generic repository** 22 | 23 | public class TestRepository : GenericRepository, IRepository 24 | { 25 | public TestRepository(TestDbContext context) 26 | : base(context) 27 | { 28 | } 29 | } 30 | 31 | 1. Use repository 32 | 33 | using(var dbContext = new MyDbContext()) 34 | { 35 | var repository = new TestRepository(dbContext); 36 | 37 | //// Support basic CRUD operation 38 | var data = repository.Get(1) 39 | repository.Add(myData); 40 | repository.Update(myData); 41 | repository.Delete(1) 42 | } 43 | 44 | Scenarios 45 | -------- 46 | 47 | Scenario: Add data into database should be success 48 | Given I have test datas 49 | | Id | Content | 50 | | 1 | TestData | 51 | When I use generic repository to add data 52 | And I save the changes 53 | Then database should exists test datas 54 | | Id | Content | 55 | | 1 | TestData | 56 | 57 | Scenario: Add datalist into database should be success 58 | Given I have test datas 59 | | Id | Content | 60 | | 1 | TestData | 61 | | 2 | TestData 2 | 62 | When I use generic repository to add datalist 63 | And I save the changes 64 | Then database should exists test datas 65 | | Id | Content | 66 | | 1 | TestData | 67 | | 2 | TestData 2 | 68 | 69 | Scenario: Get datalist from database should be success 70 | Given database has test datas 71 | | Id | Content | 72 | | 1 | TestData | 73 | | 2 | TestData 2 | 74 | When I use generic repository get data list from database 75 | Then the data list I get should be 76 | | Id | Content | 77 | | 1 | TestData | 78 | | 2 | TestData 2 | 79 | 80 | Scenario: Get datalist from database with condition content should contains "2" should be success 81 | Given database has test datas 82 | | Id | Content | 83 | | 1 | TestData | 84 | | 2 | TestData 2 | 85 | And test datas content field should contains "2" 86 | When I use generic repository get data list with condition from database 87 | Then the data list I get should be 88 | | Id | Content | 89 | | 2 | TestData 2 | 90 | 91 | Scenario: Get data from database should be success 92 | Given database has test datas 93 | | Id | Content | 94 | | 1 | TestData | 95 | | 2 | TestData 2 | 96 | When I use generic repository get data from database by id "1" 97 | Then the data list I get should be 98 | | Id | Content | 99 | | 1 | TestData | 100 | 101 | Scenario: Get data from database with condition content should contains "2" should be success 102 | Given database has test datas 103 | | Id | Content | 104 | | 1 | TestData | 105 | | 2 | TestData 2 | 106 | And test datas content field should contains "2" 107 | When I use generic repository get data from database with conditon 108 | Then the data list I get should be 109 | | Id | Content | 110 | | 2 | TestData 2 | 111 | 112 | Scenario: Update data which is exists in database should be success 113 | Given database has test datas 114 | | Id | Content | 115 | | 1 | TestData | 116 | | 2 | TestData 2 | 117 | And the data I want to update is 118 | | Id | Content | 119 | | 1 | TestData Modified | 120 | When I use generic repository update data 121 | And I save the changes 122 | Then database should exists test datas 123 | | Id | Content | 124 | | 1 | TestData Modified | 125 | | 2 | TestData 2 | 126 | 127 | Scenario: Delete data which is exists in database should be success 128 | Given database has test datas 129 | | Id | Content | 130 | | 1 | TestData | 131 | | 2 | TestData 2 | 132 | When I use generic repository delete data with id "1" 133 | And I save the changes 134 | Then database should exists test datas 135 | | Id | Content | 136 | | 2 | TestData 2 | -------------------------------------------------------------------------------- /EFRepository.Tests/Repositories/GenericRepository.feature.cs: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by SpecFlow (http://www.specflow.org/). 4 | // SpecFlow Version:2.1.0.0 5 | // SpecFlow Generator Version:2.0.0.0 6 | // 7 | // Changes to this file may cause incorrect behavior and will be lost if 8 | // the code is regenerated. 9 | // 10 | // ------------------------------------------------------------------------------ 11 | #region Designer generated code 12 | #pragma warning disable 13 | namespace EFRepository.Tests.Repositories 14 | { 15 | using TechTalk.SpecFlow; 16 | 17 | 18 | [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "2.1.0.0")] 19 | [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 20 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestClassAttribute()] 21 | public partial class GenericRepositoryFeature 22 | { 23 | 24 | private static TechTalk.SpecFlow.ITestRunner testRunner; 25 | 26 | #line 1 "GenericRepository.feature" 27 | #line hidden 28 | 29 | [Microsoft.VisualStudio.TestTools.UnitTesting.ClassInitializeAttribute()] 30 | public static void FeatureSetup(Microsoft.VisualStudio.TestTools.UnitTesting.TestContext testContext) 31 | { 32 | testRunner = TechTalk.SpecFlow.TestRunnerManager.GetTestRunner(null, 0); 33 | TechTalk.SpecFlow.FeatureInfo featureInfo = new TechTalk.SpecFlow.FeatureInfo(new System.Globalization.CultureInfo("en-US"), "GenericRepository", @"As a programmer
34 | In order to reduce the work of writing repository
35 | I would like to create a GenericRepository to process database related logic
36 | 37 | How to use? 38 | -------- 39 | 40 | 1. Create data class inherits **IEntity** 41 | 42 | public class TestData : IEntity 43 | { 44 | [Key] 45 | public int Id { get; set; } 46 | 47 | public string Content { get; set; } 48 | } 49 | 50 | 1. Create repository inherits **Generic repository** 51 | 52 | public class TestRepository : GenericRepository, IRepository 53 | { 54 | public TestRepository(TestDbContext context) 55 | : base(context) 56 | { 57 | } 58 | } 59 | 60 | 1. Use repository 61 | 62 | using(var dbContext = new MyDbContext()) 63 | { 64 | var repository = new TestRepository(dbContext); 65 | 66 | //// Support basic CRUD operation 67 | var data = repository.Get(1) 68 | repository.Add(myData); 69 | repository.Update(myData); 70 | repository.Delete(1) 71 | } 72 | 73 | Scenarios 74 | --------", ProgrammingLanguage.CSharp, new string[] { 75 | "Repository"}); 76 | testRunner.OnFeatureStart(featureInfo); 77 | } 78 | 79 | [Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupAttribute()] 80 | public static void FeatureTearDown() 81 | { 82 | testRunner.OnFeatureEnd(); 83 | testRunner = null; 84 | } 85 | 86 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestInitializeAttribute()] 87 | public virtual void TestInitialize() 88 | { 89 | if (((testRunner.FeatureContext != null) 90 | && (testRunner.FeatureContext.FeatureInfo.Title != "GenericRepository"))) 91 | { 92 | EFRepository.Tests.Repositories.GenericRepositoryFeature.FeatureSetup(null); 93 | } 94 | } 95 | 96 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestCleanupAttribute()] 97 | public virtual void ScenarioTearDown() 98 | { 99 | testRunner.OnScenarioEnd(); 100 | } 101 | 102 | public virtual void ScenarioSetup(TechTalk.SpecFlow.ScenarioInfo scenarioInfo) 103 | { 104 | testRunner.OnScenarioStart(scenarioInfo); 105 | } 106 | 107 | public virtual void ScenarioCleanup() 108 | { 109 | testRunner.CollectScenarioErrors(); 110 | } 111 | 112 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()] 113 | [Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Add data into database should be success")] 114 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "GenericRepository")] 115 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestCategoryAttribute("Repository")] 116 | public virtual void AddDataIntoDatabaseShouldBeSuccess() 117 | { 118 | TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Add data into database should be success", ((string[])(null))); 119 | #line 47 120 | this.ScenarioSetup(scenarioInfo); 121 | #line hidden 122 | TechTalk.SpecFlow.Table table1 = new TechTalk.SpecFlow.Table(new string[] { 123 | "Id", 124 | "Content"}); 125 | table1.AddRow(new string[] { 126 | "1", 127 | "TestData"}); 128 | #line 48 129 | testRunner.Given("I have test datas", ((string)(null)), table1, "Given "); 130 | #line 51 131 | testRunner.When("I use generic repository to add data", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); 132 | #line 52 133 | testRunner.And("I save the changes", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); 134 | #line hidden 135 | TechTalk.SpecFlow.Table table2 = new TechTalk.SpecFlow.Table(new string[] { 136 | "Id", 137 | "Content"}); 138 | table2.AddRow(new string[] { 139 | "1", 140 | "TestData"}); 141 | #line 53 142 | testRunner.Then("database should exists test datas", ((string)(null)), table2, "Then "); 143 | #line hidden 144 | this.ScenarioCleanup(); 145 | } 146 | 147 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()] 148 | [Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Add datalist into database should be success")] 149 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "GenericRepository")] 150 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestCategoryAttribute("Repository")] 151 | public virtual void AddDatalistIntoDatabaseShouldBeSuccess() 152 | { 153 | TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Add datalist into database should be success", ((string[])(null))); 154 | #line 57 155 | this.ScenarioSetup(scenarioInfo); 156 | #line hidden 157 | TechTalk.SpecFlow.Table table3 = new TechTalk.SpecFlow.Table(new string[] { 158 | "Id", 159 | "Content"}); 160 | table3.AddRow(new string[] { 161 | "1", 162 | "TestData"}); 163 | table3.AddRow(new string[] { 164 | "2", 165 | "TestData 2"}); 166 | #line 58 167 | testRunner.Given("I have test datas", ((string)(null)), table3, "Given "); 168 | #line 62 169 | testRunner.When("I use generic repository to add datalist", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); 170 | #line 63 171 | testRunner.And("I save the changes", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); 172 | #line hidden 173 | TechTalk.SpecFlow.Table table4 = new TechTalk.SpecFlow.Table(new string[] { 174 | "Id", 175 | "Content"}); 176 | table4.AddRow(new string[] { 177 | "1", 178 | "TestData"}); 179 | table4.AddRow(new string[] { 180 | "2", 181 | "TestData 2"}); 182 | #line 64 183 | testRunner.Then("database should exists test datas", ((string)(null)), table4, "Then "); 184 | #line hidden 185 | this.ScenarioCleanup(); 186 | } 187 | 188 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()] 189 | [Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Get datalist from database should be success")] 190 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "GenericRepository")] 191 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestCategoryAttribute("Repository")] 192 | public virtual void GetDatalistFromDatabaseShouldBeSuccess() 193 | { 194 | TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Get datalist from database should be success", ((string[])(null))); 195 | #line 69 196 | this.ScenarioSetup(scenarioInfo); 197 | #line hidden 198 | TechTalk.SpecFlow.Table table5 = new TechTalk.SpecFlow.Table(new string[] { 199 | "Id", 200 | "Content"}); 201 | table5.AddRow(new string[] { 202 | "1", 203 | "TestData"}); 204 | table5.AddRow(new string[] { 205 | "2", 206 | "TestData 2"}); 207 | #line 70 208 | testRunner.Given("database has test datas", ((string)(null)), table5, "Given "); 209 | #line 74 210 | testRunner.When("I use generic repository get data list from database", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); 211 | #line hidden 212 | TechTalk.SpecFlow.Table table6 = new TechTalk.SpecFlow.Table(new string[] { 213 | "Id", 214 | "Content"}); 215 | table6.AddRow(new string[] { 216 | "1", 217 | "TestData"}); 218 | table6.AddRow(new string[] { 219 | "2", 220 | "TestData 2"}); 221 | #line 75 222 | testRunner.Then("the data list I get should be", ((string)(null)), table6, "Then "); 223 | #line hidden 224 | this.ScenarioCleanup(); 225 | } 226 | 227 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()] 228 | [Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Get datalist from database with condition content should contains \"2\" should be s" + 229 | "uccess")] 230 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "GenericRepository")] 231 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestCategoryAttribute("Repository")] 232 | public virtual void GetDatalistFromDatabaseWithConditionContentShouldContains2ShouldBeSuccess() 233 | { 234 | TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Get datalist from database with condition content should contains \"2\" should be s" + 235 | "uccess", ((string[])(null))); 236 | #line 80 237 | this.ScenarioSetup(scenarioInfo); 238 | #line hidden 239 | TechTalk.SpecFlow.Table table7 = new TechTalk.SpecFlow.Table(new string[] { 240 | "Id", 241 | "Content"}); 242 | table7.AddRow(new string[] { 243 | "1", 244 | "TestData"}); 245 | table7.AddRow(new string[] { 246 | "2", 247 | "TestData 2"}); 248 | #line 81 249 | testRunner.Given("database has test datas", ((string)(null)), table7, "Given "); 250 | #line 85 251 | testRunner.And("test datas content field should contains \"2\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); 252 | #line 86 253 | testRunner.When("I use generic repository get data list with condition from database", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); 254 | #line hidden 255 | TechTalk.SpecFlow.Table table8 = new TechTalk.SpecFlow.Table(new string[] { 256 | "Id", 257 | "Content"}); 258 | table8.AddRow(new string[] { 259 | "2", 260 | "TestData 2"}); 261 | #line 87 262 | testRunner.Then("the data list I get should be", ((string)(null)), table8, "Then "); 263 | #line hidden 264 | this.ScenarioCleanup(); 265 | } 266 | 267 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()] 268 | [Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Get data from database should be success")] 269 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "GenericRepository")] 270 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestCategoryAttribute("Repository")] 271 | public virtual void GetDataFromDatabaseShouldBeSuccess() 272 | { 273 | TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Get data from database should be success", ((string[])(null))); 274 | #line 91 275 | this.ScenarioSetup(scenarioInfo); 276 | #line hidden 277 | TechTalk.SpecFlow.Table table9 = new TechTalk.SpecFlow.Table(new string[] { 278 | "Id", 279 | "Content"}); 280 | table9.AddRow(new string[] { 281 | "1", 282 | "TestData"}); 283 | table9.AddRow(new string[] { 284 | "2", 285 | "TestData 2"}); 286 | #line 92 287 | testRunner.Given("database has test datas", ((string)(null)), table9, "Given "); 288 | #line 96 289 | testRunner.When("I use generic repository get data from database by id \"1\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); 290 | #line hidden 291 | TechTalk.SpecFlow.Table table10 = new TechTalk.SpecFlow.Table(new string[] { 292 | "Id", 293 | "Content"}); 294 | table10.AddRow(new string[] { 295 | "1", 296 | "TestData"}); 297 | #line 97 298 | testRunner.Then("the data list I get should be", ((string)(null)), table10, "Then "); 299 | #line hidden 300 | this.ScenarioCleanup(); 301 | } 302 | 303 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()] 304 | [Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Get data from database with condition content should contains \"2\" should be succe" + 305 | "ss")] 306 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "GenericRepository")] 307 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestCategoryAttribute("Repository")] 308 | public virtual void GetDataFromDatabaseWithConditionContentShouldContains2ShouldBeSuccess() 309 | { 310 | TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Get data from database with condition content should contains \"2\" should be succe" + 311 | "ss", ((string[])(null))); 312 | #line 101 313 | this.ScenarioSetup(scenarioInfo); 314 | #line hidden 315 | TechTalk.SpecFlow.Table table11 = new TechTalk.SpecFlow.Table(new string[] { 316 | "Id", 317 | "Content"}); 318 | table11.AddRow(new string[] { 319 | "1", 320 | "TestData"}); 321 | table11.AddRow(new string[] { 322 | "2", 323 | "TestData 2"}); 324 | #line 102 325 | testRunner.Given("database has test datas", ((string)(null)), table11, "Given "); 326 | #line 106 327 | testRunner.And("test datas content field should contains \"2\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); 328 | #line 107 329 | testRunner.When("I use generic repository get data from database with conditon", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); 330 | #line hidden 331 | TechTalk.SpecFlow.Table table12 = new TechTalk.SpecFlow.Table(new string[] { 332 | "Id", 333 | "Content"}); 334 | table12.AddRow(new string[] { 335 | "2", 336 | "TestData 2"}); 337 | #line 108 338 | testRunner.Then("the data list I get should be", ((string)(null)), table12, "Then "); 339 | #line hidden 340 | this.ScenarioCleanup(); 341 | } 342 | 343 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()] 344 | [Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Update data which is exists in database should be success")] 345 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "GenericRepository")] 346 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestCategoryAttribute("Repository")] 347 | public virtual void UpdateDataWhichIsExistsInDatabaseShouldBeSuccess() 348 | { 349 | TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Update data which is exists in database should be success", ((string[])(null))); 350 | #line 112 351 | this.ScenarioSetup(scenarioInfo); 352 | #line hidden 353 | TechTalk.SpecFlow.Table table13 = new TechTalk.SpecFlow.Table(new string[] { 354 | "Id", 355 | "Content"}); 356 | table13.AddRow(new string[] { 357 | "1", 358 | "TestData"}); 359 | table13.AddRow(new string[] { 360 | "2", 361 | "TestData 2"}); 362 | #line 113 363 | testRunner.Given("database has test datas", ((string)(null)), table13, "Given "); 364 | #line hidden 365 | TechTalk.SpecFlow.Table table14 = new TechTalk.SpecFlow.Table(new string[] { 366 | "Id", 367 | "Content"}); 368 | table14.AddRow(new string[] { 369 | "1", 370 | "TestData Modified"}); 371 | #line 117 372 | testRunner.And("the data I want to update is", ((string)(null)), table14, "And "); 373 | #line 120 374 | testRunner.When("I use generic repository update data", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); 375 | #line 121 376 | testRunner.And("I save the changes", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); 377 | #line hidden 378 | TechTalk.SpecFlow.Table table15 = new TechTalk.SpecFlow.Table(new string[] { 379 | "Id", 380 | "Content"}); 381 | table15.AddRow(new string[] { 382 | "1", 383 | "TestData Modified"}); 384 | table15.AddRow(new string[] { 385 | "2", 386 | "TestData 2"}); 387 | #line 122 388 | testRunner.Then("database should exists test datas", ((string)(null)), table15, "Then "); 389 | #line hidden 390 | this.ScenarioCleanup(); 391 | } 392 | 393 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()] 394 | [Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Delete data which is exists in database should be success")] 395 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "GenericRepository")] 396 | [Microsoft.VisualStudio.TestTools.UnitTesting.TestCategoryAttribute("Repository")] 397 | public virtual void DeleteDataWhichIsExistsInDatabaseShouldBeSuccess() 398 | { 399 | TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Delete data which is exists in database should be success", ((string[])(null))); 400 | #line 127 401 | this.ScenarioSetup(scenarioInfo); 402 | #line hidden 403 | TechTalk.SpecFlow.Table table16 = new TechTalk.SpecFlow.Table(new string[] { 404 | "Id", 405 | "Content"}); 406 | table16.AddRow(new string[] { 407 | "1", 408 | "TestData"}); 409 | table16.AddRow(new string[] { 410 | "2", 411 | "TestData 2"}); 412 | #line 128 413 | testRunner.Given("database has test datas", ((string)(null)), table16, "Given "); 414 | #line 132 415 | testRunner.When("I use generic repository delete data with id \"1\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When "); 416 | #line 133 417 | testRunner.And("I save the changes", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And "); 418 | #line hidden 419 | TechTalk.SpecFlow.Table table17 = new TechTalk.SpecFlow.Table(new string[] { 420 | "Id", 421 | "Content"}); 422 | table17.AddRow(new string[] { 423 | "2", 424 | "TestData 2"}); 425 | #line 134 426 | testRunner.Then("database should exists test datas", ((string)(null)), table17, "Then "); 427 | #line hidden 428 | this.ScenarioCleanup(); 429 | } 430 | } 431 | } 432 | #pragma warning restore 433 | #endregion 434 | -------------------------------------------------------------------------------- /EFRepository.Tests/Repositories/GenericRepositorySteps.cs: -------------------------------------------------------------------------------- 1 | using EFRepository.Tests.TestClasses; 2 | using System; 3 | using System.Linq; 4 | using TechTalk.SpecFlow; 5 | using TechTalk.SpecFlow.Assist; 6 | using System.Collections.Generic; 7 | using System.Linq.Expressions; 8 | 9 | namespace EFRepository.Tests.Repositories 10 | { 11 | [Binding] 12 | [Scope(Feature = "GenericRepository")] 13 | public class GenericRepositorySteps 14 | { 15 | public string ConnectionString { get; private set; } 16 | 17 | public TestDbContext DbContext { get; private set; } 18 | 19 | public TestRepository Repository { get; private set; } 20 | 21 | public Expression> Condition { get; set; } 22 | 23 | public TestData DataItem { get; private set; } 24 | 25 | public IEnumerable DataList { get; private set; } 26 | 27 | [BeforeScenario] 28 | public void BeforeScenario() 29 | { 30 | this.ConnectionString = Environment.GetEnvironmentVariable("TestDb") ?? "TestDb"; 31 | 32 | this.DbContext = new TestDbContext(ConnectionString); 33 | this.DbContext.Database.Delete(); 34 | 35 | this.Repository = new TestRepository(this.DbContext); 36 | } 37 | 38 | [AfterScenario] 39 | public void AfterScenario() 40 | { 41 | this.DbContext.Dispose(); 42 | } 43 | 44 | [Given(@"I have test datas")] 45 | public void GivenIHaveTestDatas(Table table) 46 | { 47 | this.DataList = table.CreateSet(); 48 | } 49 | 50 | [Given(@"database has test datas")] 51 | public void GivenDatabaseHasTestDatas(Table table) 52 | { 53 | var dataList = table.CreateSet(); 54 | 55 | using (var dbContext = new TestDbContext(this.ConnectionString)) 56 | { 57 | dbContext.TestDatas.AddRange(dataList); 58 | dbContext.SaveChanges(); 59 | } 60 | } 61 | 62 | [Given(@"test datas content field should contains ""(.*)""")] 63 | public void GivenTestDatasContentFieldShouldContains(string condition) 64 | { 65 | this.Condition = (data) => data.Content.Contains(condition); 66 | } 67 | 68 | [Given(@"the data I want to update is")] 69 | public void GivenTheDataIWantToUpdateIs(Table table) 70 | { 71 | this.DataItem = table.CreateInstance(); 72 | } 73 | 74 | [When(@"I use generic repository to add data")] 75 | public void WhenIUseGenericRepositoryToAddData() 76 | { 77 | this.Repository.Add(this.DataList.First()); 78 | } 79 | 80 | [When(@"I use generic repository to add datalist")] 81 | public void WhenIUseGenericRepositoryToAddDatalist() 82 | { 83 | this.Repository.AddRange(this.DataList); 84 | } 85 | 86 | [When(@"I save the changes")] 87 | public void WhenISaveTheChanges() 88 | { 89 | this.Repository.SaveChanges(); 90 | } 91 | 92 | [When(@"I use generic repository get data list from database")] 93 | public void WhenIUseGenericRepositoryGetDataListFromDatabase() 94 | { 95 | this.DataList = this.Repository.GetList(); 96 | } 97 | 98 | [When(@"I use generic repository get data from database by id ""(.*)""")] 99 | public void WhenIUseGenericRepositoryGetDataFromDatabaseById(int id) 100 | { 101 | this.DataItem = this.Repository.Get(id); 102 | 103 | this.DataList = new List() { this.DataItem }; 104 | } 105 | 106 | [When(@"I use generic repository get data list with condition from database")] 107 | public void WhenIUseGenericRepositoryGetDataListWithConditionFromDatabase() 108 | { 109 | this.DataList = this.Repository.GetList(this.Condition); 110 | } 111 | 112 | [When(@"I use generic repository get data from database with conditon")] 113 | public void WhenIUseGenericRepositoryGetDataFromDatabaseWithConditon() 114 | { 115 | this.DataItem = this.Repository.Get(this.Condition); 116 | 117 | this.DataList = new List() { this.DataItem }; 118 | } 119 | 120 | [When(@"I use generic repository update data")] 121 | public void WhenIUseGenericRepositoryUpdateData() 122 | { 123 | this.Repository.Update(this.DataItem); 124 | } 125 | 126 | [When(@"I use generic repository delete data with id ""(.*)""")] 127 | public void WhenIUseGenericRepositoryDeleteDataWithId(int id) 128 | { 129 | this.Repository.Delete(id); 130 | } 131 | 132 | [Then(@"database should exists test datas")] 133 | public void ThenDatabaseShouldExistsTestDatas(Table table) 134 | { 135 | using (var dbContext = new TestDbContext(this.ConnectionString)) 136 | { 137 | var datalist = dbContext.TestDatas.ToList(); 138 | 139 | table.CompareToSet(datalist); 140 | } 141 | } 142 | 143 | [Then(@"the data list I get should be")] 144 | public void ThenTheDataListIGetShouldBe(Table table) 145 | { 146 | table.CompareToSet(this.DataList); 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /EFRepository.Tests/TestClasses/SoftDeleteData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace EFRepository.Tests.TestClasses 9 | { 10 | public class SoftDeleteData : IEntity, ISoftDelete 11 | { 12 | /// 13 | /// Gets or sets the identifier. 14 | /// 15 | /// 16 | /// The identifier. 17 | /// 18 | [Key] 19 | public int Id { get; set; } 20 | 21 | /// 22 | /// Gets or sets the content. 23 | /// 24 | /// 25 | /// The content. 26 | /// 27 | public string Content { get; set; } 28 | 29 | /// 30 | /// Gets or sets a value indicating whether this instance is delete. 31 | /// 32 | /// 33 | /// true if this instance is delete; otherwise, false. 34 | /// 35 | public bool IsDelete { get; set; } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /EFRepository.Tests/TestClasses/SoftDeleteRepository.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 EFRepository.Tests.TestClasses 8 | { 9 | /// 10 | /// SoftDeleteRepository 11 | /// 12 | /// 13 | public class SoftDeleteRepository : GenericRepository, IRepository 14 | { 15 | /// 16 | /// Initializes a new instance of the class. 17 | /// 18 | /// The context. 19 | public SoftDeleteRepository(TestDbContext context) 20 | : base(context) 21 | { 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /EFRepository.Tests/TestClasses/SystemInfoData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace EFRepository.Tests.TestClasses 9 | { 10 | public class SystemInfoData : IEntity, ISystemInfo 11 | { 12 | /// 13 | /// Gets or sets the identifier. 14 | /// 15 | /// 16 | /// The identifier. 17 | /// 18 | [Key] 19 | public int Id { get; set; } 20 | 21 | /// 22 | /// Gets or sets the content. 23 | /// 24 | /// 25 | /// The content. 26 | /// 27 | public string Content { get; set; } 28 | 29 | /// 30 | /// Gets or sets the created at. 31 | /// 32 | /// 33 | /// The created at. 34 | /// 35 | public DateTime CreatedAt { get; set; } 36 | 37 | /// 38 | /// Gets or sets the created by. 39 | /// 40 | /// 41 | /// The created by. 42 | /// 43 | public string CreatedBy { get; set; } 44 | 45 | /// 46 | /// Gets or sets the updated at. 47 | /// 48 | /// 49 | /// The updated at. 50 | /// 51 | public DateTime UpdatedAt { get; set; } 52 | 53 | /// 54 | /// Gets or sets the updated by. 55 | /// 56 | /// 57 | /// The updated by. 58 | /// 59 | public string UpdatedBy { get; set; } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /EFRepository.Tests/TestClasses/SystemInfoRepository.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 EFRepository.Tests.TestClasses 8 | { 9 | /// 10 | /// SystemInfoRepository 11 | /// 12 | /// 13 | /// 14 | public class SystemInfoRepository : GenericRepository, IRepository 15 | { 16 | /// 17 | /// Initializes a new instance of the class. 18 | /// 19 | /// The context. 20 | public SystemInfoRepository(TestDbContext context) 21 | : base(context) 22 | { 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /EFRepository.Tests/TestClasses/TestData.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace EFRepository.Tests.TestClasses 4 | { 5 | /// 6 | /// TestData 7 | /// 8 | public class TestData : IEntity 9 | { 10 | /// 11 | /// Gets or sets the identifier. 12 | /// 13 | /// 14 | /// The identifier. 15 | /// 16 | [Key] 17 | public int Id { get; set; } 18 | 19 | /// 20 | /// Gets or sets the content. 21 | /// 22 | /// 23 | /// The content. 24 | /// 25 | public string Content { get; set; } 26 | } 27 | } -------------------------------------------------------------------------------- /EFRepository.Tests/TestClasses/TestDbContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data.Entity; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace EFRepository.Tests.TestClasses 9 | { 10 | /// 11 | /// TestDbContext 12 | /// 13 | /// 14 | public class TestDbContext: DbContext 15 | { 16 | /// 17 | /// Initializes a new instance of the class. 18 | /// 19 | public TestDbContext(string nameOrConnectionString) 20 | : base(nameOrConnectionString) 21 | { 22 | } 23 | 24 | /// 25 | /// Gets or sets the test datas. 26 | /// 27 | /// 28 | /// The test datas. 29 | /// 30 | public DbSet TestDatas { get; set; } 31 | 32 | /// 33 | /// Gets or sets the soft delete datas. 34 | /// 35 | /// 36 | /// The soft delete datas. 37 | /// 38 | public DbSet SoftDeleteDatas { get; set; } 39 | 40 | /// 41 | /// Gets or sets the system information datas. 42 | /// 43 | /// 44 | /// The system information datas. 45 | /// 46 | public DbSet SystemInfoDatas { get; set; } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /EFRepository.Tests/TestClasses/TestRepository.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 EFRepository.Tests.TestClasses 8 | { 9 | /// 10 | /// TestRepository 11 | /// 12 | /// 13 | public class TestRepository : GenericRepository, IRepository 14 | { 15 | /// 16 | /// Initializes a new instance of the class. 17 | /// 18 | /// The context. 19 | public TestRepository(TestDbContext context) 20 | : base(context) 21 | { 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /EFRepository.Tests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /EFRepository.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25123.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EFRepository", "EFRepository\EFRepository.csproj", "{3D6FC108-03AB-47E5-96C5-401CB8793BD7}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EFRepository.Tests", "EFRepository.Tests\EFRepository.Tests.csproj", "{BC65819A-550B-4DA1-9AD4-A29EE244AD21}" 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 | {3D6FC108-03AB-47E5-96C5-401CB8793BD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {3D6FC108-03AB-47E5-96C5-401CB8793BD7}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {3D6FC108-03AB-47E5-96C5-401CB8793BD7}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {3D6FC108-03AB-47E5-96C5-401CB8793BD7}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {BC65819A-550B-4DA1-9AD4-A29EE244AD21}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {BC65819A-550B-4DA1-9AD4-A29EE244AD21}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {BC65819A-550B-4DA1-9AD4-A29EE244AD21}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {BC65819A-550B-4DA1-9AD4-A29EE244AD21}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /EFRepository/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /EFRepository/DatetimeHelper.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 EFRepository 8 | { 9 | /// 10 | /// DatetimeHelper 11 | /// 12 | /// 13 | public class DatetimeHelper : IDatetimeHelper 14 | { 15 | /// 16 | /// Gets the current time. 17 | /// 18 | /// current time 19 | public DateTime GetCurrentTime() 20 | { 21 | return DateTime.Now; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /EFRepository/EFRepository.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {3D6FC108-03AB-47E5-96C5-401CB8793BD7} 8 | Library 9 | Properties 10 | EFRepository 11 | EFRepository 12 | v4.6.1 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\EntityFramework.6.1.3\lib\net45\EntityFramework.dll 35 | True 36 | 37 | 38 | ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll 39 | True 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 81 | -------------------------------------------------------------------------------- /EFRepository/GenericRepository.cs: -------------------------------------------------------------------------------- 1 | using EFRepository.Hooks; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Data.Entity; 5 | using System.Linq; 6 | using System.Linq.Expressions; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace EFRepository 11 | { 12 | /// 13 | /// GenericRepository 14 | /// 15 | /// The type of the entity. 16 | public class GenericRepository : IRepository where TEntity : class, IEntity where TKey : IEquatable 17 | { 18 | /// 19 | /// Gets the database context. 20 | /// 21 | /// 22 | /// The database context. 23 | /// 24 | internal DbContext DbContext { get; set; } 25 | 26 | /// 27 | /// Gets or sets the post action hooks. 28 | /// 29 | /// 30 | /// The post action hooks. 31 | /// 32 | public ICollection> PostActionHooks { get; set; } 33 | 34 | /// 35 | /// Gets or sets the post load hooks. 36 | /// 37 | /// 38 | /// The post load hooks. 39 | /// 40 | public ICollection> PostLoadHooks { get; set; } 41 | 42 | /// 43 | /// Initializes a new instance of the class. 44 | /// 45 | /// The context. 46 | public GenericRepository(DbContext context) 47 | { 48 | this.DbContext = context; 49 | this.PostActionHooks = new List>(); 50 | this.PostLoadHooks = new List>(); 51 | } 52 | 53 | /// 54 | /// Registers the post load hook. 55 | /// 56 | /// The hook. 57 | public void RegisterPostLoadHook(IPostLoadHook hook) 58 | { 59 | this.PostLoadHooks.Add(hook); 60 | } 61 | 62 | /// 63 | /// Registers the post action hook. 64 | /// 65 | /// The hook. 66 | public void RegisterPostActionHook(IPostActionHook hook) 67 | { 68 | this.PostActionHooks.Add(hook); 69 | } 70 | 71 | /// 72 | /// Adds the specified data. 73 | /// 74 | /// The data. 75 | public virtual void Add(TEntity data) 76 | { 77 | this.DbContext.Set().Add(data); 78 | 79 | foreach (var hook in this.PostActionHooks) 80 | { 81 | hook.Execute(data, new HookContext() 82 | { 83 | DbContext = this.DbContext, 84 | Entity = data 85 | }); 86 | } 87 | } 88 | 89 | /// 90 | /// Adds the range. 91 | /// 92 | /// The datalist. 93 | public virtual void AddRange(IEnumerable datalist) 94 | { 95 | foreach (var data in datalist) 96 | { 97 | this.Add(data); 98 | } 99 | } 100 | 101 | /// 102 | /// Gets the list. 103 | /// 104 | /// data list 105 | public virtual IEnumerable GetList() 106 | { 107 | var query = this.DbContext.Set() 108 | .AsQueryable(); 109 | 110 | foreach (var hook in this.PostLoadHooks) 111 | { 112 | query = hook.Execute(query, new HookContext() 113 | { 114 | DbContext = this.DbContext, 115 | Entity = query 116 | }); 117 | } 118 | 119 | return query.ToList(); 120 | } 121 | 122 | /// 123 | /// Gets the list. 124 | /// 125 | /// The where. 126 | /// data list 127 | public virtual IEnumerable GetList(Expression> condition) 128 | { 129 | var query = this.DbContext.Set() 130 | .Where(condition); 131 | 132 | foreach (var hook in this.PostLoadHooks) 133 | { 134 | query = hook.Execute(query, new HookContext() 135 | { 136 | DbContext = this.DbContext, 137 | Entity = query 138 | }); 139 | } 140 | 141 | return query.ToList(); 142 | } 143 | 144 | /// 145 | /// Gets the specified identifier. 146 | /// 147 | /// The type of the key. 148 | /// The identifier. 149 | /// data 150 | public virtual TEntity Get(TKey id) 151 | { 152 | var query = this.DbContext.Set() 153 | .Where(i => i.Id.Equals(id)); 154 | 155 | foreach (var hook in this.PostLoadHooks) 156 | { 157 | query = hook.Execute(query, new HookContext() 158 | { 159 | DbContext = this.DbContext, 160 | Entity = query 161 | }); 162 | } 163 | 164 | return query.FirstOrDefault(); 165 | } 166 | 167 | /// 168 | /// Gets the specified where. 169 | /// 170 | /// The where. 171 | /// data 172 | public virtual TEntity Get(Expression> condition) 173 | { 174 | var query = this.DbContext.Set() 175 | .Where(condition); 176 | 177 | foreach (var hook in this.PostLoadHooks) 178 | { 179 | query = hook.Execute(query, new HookContext() 180 | { 181 | DbContext = this.DbContext, 182 | Entity = query 183 | }); 184 | } 185 | 186 | return query.FirstOrDefault(); 187 | } 188 | 189 | /// 190 | /// Updates the specified data. 191 | /// 192 | /// The data. 193 | public virtual void Update(TEntity data) 194 | { 195 | this.DbContext.Set().Attach(data); 196 | 197 | var entry = this.DbContext.Entry(data); 198 | entry.State = EntityState.Modified; 199 | 200 | foreach (var hook in this.PostActionHooks) 201 | { 202 | hook.Execute(data, new HookContext() 203 | { 204 | DbContext = this.DbContext, 205 | Entity = data 206 | }); 207 | } 208 | } 209 | 210 | /// 211 | /// Deletes the specified identifier. 212 | /// 213 | /// The identifier. 214 | public virtual void Delete(TKey id) 215 | { 216 | var data = this.Get(id); 217 | 218 | this.DbContext.Set() 219 | .Remove(data); 220 | 221 | foreach (var hook in this.PostActionHooks) 222 | { 223 | hook.Execute(data, new HookContext() 224 | { 225 | DbContext = this.DbContext, 226 | Entity = data 227 | }); 228 | }; 229 | } 230 | 231 | /// 232 | /// Saves the changes. 233 | /// 234 | /// the count of effected rows 235 | public int SaveChanges() 236 | { 237 | var result = this.DbContext.SaveChanges(); 238 | 239 | return result; 240 | } 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /EFRepository/Hooks/HookContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Data.Entity; 4 | 5 | namespace EFRepository.Hooks 6 | { 7 | public class HookContext 8 | { 9 | public object Entity { get; set; } 10 | 11 | public DbContext DbContext { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /EFRepository/Hooks/IPostActionHook.cs: -------------------------------------------------------------------------------- 1 | namespace EFRepository.Hooks 2 | { 3 | public interface IPostActionHook where TEntity : class 4 | { 5 | void Execute(TEntity entity, HookContext context); 6 | } 7 | } -------------------------------------------------------------------------------- /EFRepository/Hooks/IPostLoadHook.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace EFRepository.Hooks 9 | { 10 | public interface IPostLoadHook where TEntity: class 11 | { 12 | IQueryable Execute(IQueryable entity, HookContext context); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /EFRepository/Hooks/SoftDeletePostActionHook.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Data.Entity; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace EFRepository.Hooks 10 | { 11 | public class SoftDeletePostActionHook : IPostActionHook where TEntity : class 12 | { 13 | public void Execute(TEntity entity, HookContext context) 14 | { 15 | if (!(entity is ISoftDelete)) 16 | { 17 | return; 18 | } 19 | 20 | var entry = context.DbContext.Entry(entity); 21 | if (entry.State == EntityState.Deleted) 22 | { 23 | entry.State = EntityState.Modified; 24 | 25 | var softDeleteData = entity as ISoftDelete; 26 | softDeleteData.IsDelete = true; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /EFRepository/Hooks/SoftDeletePostLoadHook.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace EFRepository.Hooks 9 | { 10 | public class SoftDeletePostLoadHook : IPostLoadHook where TEntity : class 11 | { 12 | public IQueryable Execute(IQueryable query, HookContext context) 13 | { 14 | if (query is IQueryable) 15 | { 16 | var softDeleteQuery = query as IQueryable; 17 | softDeleteQuery = softDeleteQuery.Where(i => !i.IsDelete); 18 | 19 | query = softDeleteQuery.OfType().AsQueryable(); 20 | } 21 | 22 | return query; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /EFRepository/Hooks/SystemInfoPostActionHook.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data.Entity; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace EFRepository.Hooks 9 | { 10 | /// 11 | /// SystemInfoPostActionHook 12 | /// 13 | /// The type of the entity. 14 | /// 15 | public class SystemInfoPostActionHook : IPostActionHook where TEntity : class 16 | { 17 | /// 18 | /// Gets the user helper. 19 | /// 20 | /// 21 | /// The user helper. 22 | /// 23 | public IUserHelper UserHelper { get; private set; } 24 | 25 | public IDatetimeHelper DatetimeHelper { get; private set; } 26 | 27 | /// 28 | /// Initializes a new instance of the class. 29 | /// 30 | /// The user helper. 31 | public SystemInfoPostActionHook(IUserHelper userHelper, IDatetimeHelper datetimeHelper) 32 | { 33 | this.UserHelper = userHelper; 34 | this.DatetimeHelper = datetimeHelper; 35 | } 36 | 37 | /// 38 | /// Executes the specified entity. 39 | /// 40 | /// The entity. 41 | /// The context. 42 | public void Execute(TEntity entity, HookContext context) 43 | { 44 | if (!(entity is ISystemInfo)) 45 | { 46 | return; 47 | } 48 | 49 | var systemInfoData = entity as ISystemInfo; 50 | var entry = context.DbContext.Entry(entity); 51 | 52 | if (entry.State == EntityState.Added) 53 | { 54 | var currentTime = this.DatetimeHelper.GetCurrentTime(); 55 | var userName = this.UserHelper.GetUserName(); 56 | 57 | systemInfoData.CreatedAt = currentTime; 58 | systemInfoData.CreatedBy = userName; 59 | systemInfoData.UpdatedAt = currentTime; 60 | systemInfoData.UpdatedBy = userName; 61 | } 62 | 63 | if (entry.State == EntityState.Modified) 64 | { 65 | var currentTime = this.DatetimeHelper.GetCurrentTime(); 66 | var userName = this.UserHelper.GetUserName(); 67 | 68 | systemInfoData.UpdatedAt = currentTime; 69 | systemInfoData.UpdatedBy = userName; 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /EFRepository/IDatetimeHelper.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 EFRepository 8 | { 9 | /// 10 | /// IDatetimeHelper 11 | /// 12 | public interface IDatetimeHelper 13 | { 14 | /// 15 | /// Gets the current time. 16 | /// 17 | /// 18 | DateTime GetCurrentTime(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /EFRepository/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 EFRepository 8 | { 9 | /// 10 | /// IEntity 11 | /// 12 | /// The type of the key. 13 | public interface IEntity where TKey: IEquatable 14 | { 15 | /// 16 | /// Gets or sets the identifier. 17 | /// 18 | /// 19 | /// The identifier. 20 | /// 21 | TKey Id { get; set; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /EFRepository/IRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq.Expressions; 4 | 5 | namespace EFRepository 6 | { 7 | /// 8 | /// IRepository 9 | /// 10 | /// The type of the entity. 11 | public interface IRepository where TEntity : class, IEntity where TKey : IEquatable 12 | { 13 | /// 14 | /// Adds the specified data. 15 | /// 16 | /// The data. 17 | void Add(TEntity data); 18 | 19 | /// 20 | /// Adds the range. 21 | /// 22 | /// The datalist. 23 | void AddRange(IEnumerable datalist); 24 | 25 | /// 26 | /// Gets the list. 27 | /// 28 | /// data list 29 | IEnumerable GetList(); 30 | 31 | /// 32 | /// Gets the list. 33 | /// 34 | /// The where. 35 | /// data list 36 | IEnumerable GetList(Expression> condition); 37 | 38 | /// 39 | /// Gets the specified identifier. 40 | /// 41 | /// The type of the key. 42 | /// The identifier. 43 | /// data 44 | TEntity Get(TKey id); 45 | 46 | /// 47 | /// Gets the specified where. 48 | /// 49 | /// The where. 50 | /// data 51 | TEntity Get(Expression> condition); 52 | 53 | /// 54 | /// Updates the specified data. 55 | /// 56 | /// The data. 57 | void Update(TEntity data); 58 | 59 | /// 60 | /// Deletes the specified identifier. 61 | /// 62 | /// The identifieTKeyr. 63 | void Delete(TKey id); 64 | 65 | /// 66 | /// Saves the changes. 67 | /// 68 | /// 69 | int SaveChanges(); 70 | } 71 | } -------------------------------------------------------------------------------- /EFRepository/ISoftDelete.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 EFRepository 8 | { 9 | /// 10 | /// ISoftDelete 11 | /// 12 | public interface ISoftDelete 13 | { 14 | /// 15 | /// Gets or sets a value indicating whether this instance is delete. 16 | /// 17 | /// 18 | /// true if this instance is delete; otherwise, false. 19 | /// 20 | bool IsDelete { get; set; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /EFRepository/ISystemInfo.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 EFRepository 8 | { 9 | /// 10 | /// ISystemInfo 11 | /// 12 | public interface ISystemInfo 13 | { 14 | /// 15 | /// Gets or sets the created at. 16 | /// 17 | /// 18 | /// The created at. 19 | /// 20 | DateTime CreatedAt { get; set; } 21 | 22 | /// 23 | /// Gets or sets the created by. 24 | /// 25 | /// 26 | /// The created by. 27 | /// 28 | string CreatedBy { get; set; } 29 | 30 | /// 31 | /// Gets or sets the updated at. 32 | /// 33 | /// 34 | /// The updated at. 35 | /// 36 | DateTime UpdatedAt { get; set; } 37 | 38 | /// 39 | /// Gets or sets the updated by. 40 | /// 41 | /// 42 | /// The updated by. 43 | /// 44 | string UpdatedBy { get; set; } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /EFRepository/IUserHelper.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 EFRepository 8 | { 9 | /// 10 | /// IUserHelper 11 | /// 12 | public interface IUserHelper 13 | { 14 | /// 15 | /// Gets the name of the user. 16 | /// 17 | /// User name 18 | string GetUserName(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /EFRepository/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("EFRepository")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("EFRepository")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("3d6fc108-03ab-47e5-96c5-401cb8793bd7")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /EFRepository/UnitOfWork.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data.Entity; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace EFRepository 9 | { 10 | /// 11 | /// UnitOfWork 12 | /// 13 | /// 14 | public class UnitOfWork : IDisposable 15 | { 16 | /// 17 | /// Gets the database context. 18 | /// 19 | /// 20 | /// The database context. 21 | /// 22 | public DbContext DbContext { get; private set; } 23 | 24 | /// 25 | /// Initializes a new instance of the class. 26 | /// 27 | /// The context. 28 | public UnitOfWork(DbContext context) 29 | { 30 | this.DbContext = context; 31 | } 32 | 33 | /// 34 | /// Saves the changes. 35 | /// 36 | /// the count of effected rows 37 | public int SaveChanges() 38 | { 39 | var result = this.DbContext.SaveChanges(); 40 | 41 | return result; 42 | } 43 | 44 | /// 45 | /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. 46 | /// 47 | public void Dispose() 48 | { 49 | this.DbContext = null; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /EFRepository/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Kirk 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EFRepository 2 | 3 | [![Build status](https://ci.appveyor.com/api/projects/status/vriyn5ano6rvqarb?svg=true)](https://ci.appveyor.com/project/kirkchen/efrepository) 4 | [![Code Coverage](http://sonarcovbadge.epicapp.com/?server=sonarqube.com&resource=EFRepository&metrics=coverage&ssl=true)](https://sonarqube.com/overview?id=EFRepository) 5 | [![Technical Debt](https://img.shields.io/sonar/http/sonarqube.com/EFRepository/tech_debt.svg?maxAge=2592000)](https://sonarqube.com/overview?id=EFRepository) 6 | [![NuGet](https://img.shields.io/nuget/v/KirkChen.EFRepository.svg?maxAge=2592000)](https://www.nuget.org/packages/KirkChen.EFRepository/) 7 | [![NuGet Pre Release](https://img.shields.io/nuget/vpre/KirkChen.EFRepository.svg?maxAge=2592000)](https://www.nuget.org/packages/KirkChen.EFRepository/) 8 | [![Gitter](https://badges.gitter.im/efrepository/Lobby.svg)](https://gitter.im/efrepository/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) 9 | 10 | Generic repository and pattern "Unit of work" for Entity framework 11 | 12 | [Read more samples...](https://kirkchen.github.io/EFRepository/sample.html) 13 | 14 | ## Requirements 15 | 16 | * .Net Framework 4.6.1 17 | * EntityFramework 6.1.3 18 | 19 | ## Features 20 | 21 | * [Generic Repository](https://kirkchen.github.io/EFRepository/sample.html?feature=Repositories\GenericRepository.feature) 22 | * Basic operation 23 | * Add 24 | * Add range 25 | * Get list 26 | * Get list with condition 27 | * Get by id 28 | * Get with condition 29 | * Update 30 | * Delete 31 | * Support generic identity 32 | * Asynchronous operation 33 | * Add async 34 | * Add range async 35 | * Get list async 36 | * Get list with condition async 37 | * Get by id async 38 | * Get with condition async 39 | * Update async 40 | * Delete async 41 | * Hooks Supports 42 | * Nested object save changes 43 | * [Soft delete](https://kirkchen.github.io/EFRepository/sample.html?feature=Hooks\SoftDelete.feature) 44 | * [Auto system infomation](https://kirkchen.github.io/EFRepository/sample.html?feature=Hooks\SystemInfo.feature) 45 | * Audit log 46 | * Global query filter 47 | * Unit of work 48 | 49 | ## Quick Start 50 | 51 | 1. Install nuget package 52 | 53 | ``` 54 | Install-Package KirkChen.EFRepository 55 | ``` 56 | 57 | 1. Create data class with interface IEntity 58 | 59 | ``` csharp 60 | public class MyData : IEntity 61 | { 62 | [Key] 63 | public int Id { get; set; } 64 | 65 | public string Content { get; set; } 66 | } 67 | ``` 68 | 69 | 1. Create dbContext 70 | 71 | ``` csharp 72 | public class MyDbContext: DbContext 73 | { 74 | public DbSet MyDatas { get; set; } 75 | } 76 | ``` 77 | 78 | 1. Create repository for data class 79 | 80 | ``` csharp 81 | public class MyDataRepository : GenericRepository, IRepository 82 | { 83 | public MyDataRepository(MyDbContext context) 84 | : base(context) 85 | { 86 | // Enable soft delete 87 | this.RegisterPostLoadHook(new SoftDeletePostLoadHook()); 88 | this.RegisterPostActionHook(new SoftDeletePostActionHook()); 89 | } 90 | } 91 | ``` 92 | 93 | 1. Use reository 94 | 95 | ``` csharp 96 | var dbContext = new MyDbContext(); 97 | var repository = new MyDataRepository(dbContext); 98 | var myData = repository.Get(1); 99 | ``` 100 | 101 | ## Unit of work 102 | 103 | Using unit of work to handle transaction 104 | 105 | ``` csharp 106 | using(var dbContext = new MyDbContext()) 107 | using(var unitOfWork = new UnitOfWork(dbContext)) 108 | { 109 | var repository = new MyDataRepository(dbContext); 110 | repository.Add(data); 111 | 112 | var anotherRepository = new OtherDataRepository(dbContext); 113 | repository.Add(anotherdata); 114 | 115 | unitOfWork.SaveChanges(); 116 | } 117 | ``` 118 | 119 | ## Roadmap 120 | 121 | - [ ] Generic Repository 122 | - [x] Basic operation 123 | - [x] Add 124 | - [x] Add range 125 | - [x] Get list 126 | - [x] Get list with condition 127 | - [x] Get by id 128 | - [x] Get with condition 129 | - [x] Update 130 | - [x] Delete 131 | - [x] Support generic identity 132 | - [ ] Asynchronous operation 133 | - [ ] Add async 134 | - [ ] Add range async 135 | - [ ] Get list async 136 | - [ ] Get list with condition async 137 | - [ ] Get by id async 138 | - [ ] Get with condition async 139 | - [ ] Update async 140 | - [ ] Delete async 141 | - [ ] Hooks Supports 142 | - [ ] Nested object save changes 143 | - [x] Soft delete 144 | - [x] Auto system infomation 145 | - [ ] Audit log 146 | - [ ] Global query filter 147 | - [x] Unit of work -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.0.{build} 2 | configuration: Release 3 | 4 | environment: 5 | TestDb: Server=(local)\SQL2016;Database=efrepositorytest;User ID=sa;Password=Password12! 6 | SONAR_TOKEN: 7 | secure: obPgeJ9AhOuWX6WhYKEQGKxiiMT6nZZbPrHnIKRPr9vE1Mg1BHv2TxBrgHBUPjGG 8 | GITHUB_EMAIL: rwk0119@yahoo.com.tw 9 | GITHUB_USERNAME: kirkchen 10 | GITHUB_TOKEN: 11 | secure: YF0+KGcrx/A/5UkgdBvm2tPmoQtusFvzhQwxAfg64DW7qWiUXNb8yszDycqErx1h 12 | 13 | services: 14 | - mssql2016 15 | 16 | matrix: 17 | fast_finish: true 18 | 19 | cache: 20 | - C:\Users\appveyor\AppData\Local\NuGet\Cache 21 | 22 | assembly_info: 23 | patch: true 24 | file: AssemblyInfo.* 25 | assembly_version: "1.0.{build}" 26 | assembly_file_version: "{version}" 27 | assembly_informational_version: "{version}" 28 | 29 | install: 30 | - cinst msbuild-sonarqube-runner 31 | - cinst pickles 32 | 33 | before_build: 34 | - nuget restore 35 | - ps: if (-Not $env:APPVEYOR_PULL_REQUEST_NUMBER -And $env:APPVEYOR_REPO_BRANCH -eq "master") { .\tools\build.bat } 36 | - ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER) { .\tools\buildpullrequest.bat } 37 | 38 | build: 39 | parallel: true 40 | publish_wap: true 41 | verbosity: minimal 42 | project: EFRepository.sln 43 | 44 | test_script: 45 | - packages\OpenCover.4.6.519\tools\OpenCover.Console.exe -register:user -target:vstest.console.exe -targetargs:"/logger:Appveyor EFRepository.Tests\bin\Release\EFRepository.Tests.dll" -output:CodeCoverage.xml 46 | 47 | after_test: 48 | - ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -Or $env:APPVEYOR_REPO_BRANCH -eq "master") { .\tools\buildfinish.bat } 49 | - ps: if ($env:APPVEYOR_REPO_BRANCH -eq "develop") { .\tools\packPrereleaseNuget.bat } 50 | - ps: if ($env:APPVEYOR_REPO_TAG -eq "true") { .\tools\packNuget.bat } 51 | - ps: if (-Not $env:APPVEYOR_PULL_REQUEST_NUMBER -And $env:APPVEYOR_REPO_BRANCH -eq "master") { .\tools\generateDocs.bat } 52 | - ps: if (-Not $env:APPVEYOR_PULL_REQUEST_NUMBER -And $env:APPVEYOR_REPO_BRANCH -eq "master") { .\tools\pushDocs.ps1 $env:APPVEYOR_BUILD_FOLDER $env:GITHUB_EMAIL $env:GITHUB_USERNAME $env:GITHUB_TOKEN } 53 | 54 | artifacts: 55 | - path: 'KirkChen.EFRepository*.nupkg' 56 | 57 | deploy: 58 | - provider: NuGet 59 | api_key: 60 | secure: P4/mzIswMzqvWjuucHnGxP5duV/aBk7AnsQpCKp6R+ztVxfRocPsB5dVIIt24OGE 61 | skip_symbols: true 62 | artifact: /.*\.nupkg/ 63 | on: 64 | appveyor_repo_tag: true 65 | 66 | - provider: NuGet 67 | api_key: 68 | secure: P4/mzIswMzqvWjuucHnGxP5duV/aBk7AnsQpCKp6R+ztVxfRocPsB5dVIIt24OGE 69 | skip_symbols: true 70 | artifact: /.*\.nupkg/ 71 | on: 72 | branch: 73 | - develop 74 | 75 | notifications: 76 | - provider: Webhook 77 | url: https://webhooks.gitter.im/e/7967db8e1538fa067706 78 | on_build_success: true 79 | on_build_failure: true 80 | on_build_status_changed: true 81 | -------------------------------------------------------------------------------- /nuget/KirkChen.EFRepository.nuspec: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | KirkChen.EFRepository 5 | 1.0.0 6 | KirkChen.EFRepository 7 | Kirk Chen 8 | https://github.com/kirkchen/EFRepository 9 | false 10 | Generic repository and pattern "Unit of work" for Entity framework. 11 | en-US 12 | EF, Database, Data, Repository 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "efrepository", 3 | "version": "1.0.0", 4 | "description": "Generic repository and pattern \"Unit of work\" for Entity framework", 5 | "scripts": { 6 | "changelog": "conventional-changelog -p angular -i CHANGELOG.md -w" 7 | }, 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/kirkchen/EFRepository.git" 11 | }, 12 | "keywords": [ 13 | "EF", 14 | "Database", 15 | "Data", 16 | "Repository" 17 | ], 18 | "author": "Kirk Chen", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/kirkchen/EFRepository/issues" 22 | }, 23 | "homepage": "https://github.com/kirkchen/EFRepository#readme", 24 | "devDependencies": { 25 | "conventional-changelog-cli": "^1.2.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tools/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | MSBuild.SonarQube.Runner.exe begin /k:EFRepository /n:EFRepository /d:sonar.host.url=https://sonarqube.com /d:sonar.login=%SONAR_TOKEN% /d:sonar.cs.opencover.reportsPaths="%CD%\CodeCoverage.xml" /version:%APPVEYOR_BUILD_NUMBER% -------------------------------------------------------------------------------- /tools/buildfinish.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | MSBuild.SonarQube.Runner.exe end /d:sonar.login=%SONAR_TOKEN% -------------------------------------------------------------------------------- /tools/buildpullrequest.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | MSBuild.SonarQube.Runner.exe begin /k:EFRepository /n:EFRepository /d:sonar.analysis.mode=issues /d:sonar.issuesReport.console.enable=true /d:sonar.host.url=https://sonarqube.com /d:sonar.login=%SONAR_TOKEN% /d:sonar.cs.opencover.reportsPaths="%CD%\CodeCoverage.xml" /d:sonar.github.pullRequest=%APPVEYOR_PULL_REQUEST_NUMBER% /d:sonar.github.repository=kirkchen/EFRepository /d:sonar.github.oauth=%GITHUB_TOKEN% /version:%APPVEYOR_BUILD_NUMBER% -------------------------------------------------------------------------------- /tools/generateDocs.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | pickles run --feature-directory=EFRepository.Tests --output-directory=TestDocs --documentation-format=dhtml --sn=EFRepository -------------------------------------------------------------------------------- /tools/packNuget.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | nuget pack nuget\KirkChen.EFRepository.nuspec -Version %APPVEYOR_REPO_TAG_NAME% -------------------------------------------------------------------------------- /tools/packPrereleaseNuget.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | nuget pack nuget\KirkChen.EFRepository.nuspec -Version %APPVEYOR_BUILD_VERSION%-pre -------------------------------------------------------------------------------- /tools/pushDocs.ps1: -------------------------------------------------------------------------------- 1 | param([string]$buildFolder, [string]$email, [string]$username, [string]$personalAccessToken) 2 | 3 | Write-Host "- Set config settings...." 4 | git config --global user.email $email 5 | git config --global user.name $username 6 | git config --global push.default matching 7 | 8 | Write-Host "- Clone gh-pages branch...." 9 | cd "$($buildFolder)\..\" 10 | mkdir gh-pages 11 | git clone --quiet --branch=gh-pages https://$($username):$($personalAccessToken)@github.com/kirkchen/EFRepository.git .\gh-pages\ 12 | cd gh-pages 13 | git status 14 | 15 | Write-Host "Rename TestDocs index.html to sample.html" 16 | Rename-Item $buildFolder\TestDocs\index.html sample.html 17 | 18 | Write-Host "- Copy contents of static-site folder into gh-pages folder...." 19 | copy-item -path $buildFolder\TestDocs\* -Destination $pwd.Path -Recurse -Force 20 | 21 | git status 22 | $thereAreChanges = git status | select-string -pattern "Changes not staged for commit:","Untracked files:" -simplematch 23 | if ($thereAreChanges -ne $null) { 24 | Write-host "- Committing changes to documentation..." 25 | git add --all 26 | git status 27 | git commit -m "skip ci - static site regeneration" 28 | git status 29 | Write-Host "- Push it...." 30 | git push --quiet 31 | Write-Host "- Pushed it good!" 32 | } 33 | else { 34 | write-host "- No changes to documentation to commit" 35 | } --------------------------------------------------------------------------------