├── .gitattributes ├── .github └── workflows │ └── codeql-analysis.yml ├── .gitignore ├── BaseTests ├── CodexMicroORM.BaseTests.csproj ├── DBExtensions.CodeGen.cs ├── DBOps.cs ├── GenEntityWrappersWithAttributes.cs ├── GenProcsWrappersWithAttributes.cs ├── GlobalSuppressions.cs ├── Modern12.cs ├── POCO.cs ├── SandboxTests.cs ├── Wrappers.CodeGen.cs ├── setup.sql └── testdata1.json ├── CodexMicroORM.BindingSupport ├── CodexMicroORM.BindingSupport.csproj ├── CodexMicroORM.BindingSupport.nuspec ├── Collections.cs ├── DynamicBindable.cs ├── Extensions.cs ├── Properties │ └── AssemblyInfo.cs └── xSkrape.ico ├── CodexMicroORM.CodexV1CompatLayer └── CodexMicroORM.CodexV1CompatLayer │ ├── CodexMicroORM.CodexV1CompatLayer.csproj │ ├── Extensions.cs │ ├── GenericSet.cs │ ├── GlobalSuppressions.cs │ ├── ParallelWorkload.cs │ ├── StandardEntities.cs │ └── Stubs.cs ├── CodexMicroORM.Core ├── Base │ ├── AttributeInitializer.cs │ ├── Attributes.cs │ ├── BaseModel.cs │ ├── CEF.cs │ ├── Collections.cs │ ├── DateOnlyType.cs │ ├── Debug.cs │ ├── Enums.cs │ ├── Exceptions.cs │ ├── Extensions.cs │ ├── Globals.cs │ ├── Interfaces.cs │ ├── Performance.cs │ ├── ServiceScope.TrackedObject.cs │ ├── ServiceScope.cs │ ├── Settings.cs │ └── Support.cs ├── CachingProviders │ └── MemoryFileSystemBacked.cs ├── CodexMicroORM.Core.csproj ├── DatabaseProviders │ └── MSSQL │ │ ├── MSSQLCommand.cs │ │ ├── MSSQLConnection.cs │ │ └── MSSQLProcBasedProvider.cs ├── GlobalSuppressions.cs ├── ListServices │ └── Infrastructure │ │ ├── EntitySet.cs │ │ ├── InMemIndexes.cs │ │ └── IndexedSet.cs ├── ObjectServices │ ├── Audit.cs │ ├── DB.cs │ ├── Infrastructure │ │ ├── ConnectionScope.cs │ │ ├── DynamicWithAll.cs │ │ ├── DynamicWithBag.cs │ │ ├── DynamicWithValuesAndBag.cs │ │ ├── DynamicWithValuesBagErrors.cs │ │ ├── FieldWrapper.cs │ │ ├── ValidationWrapper.cs │ │ └── WrappingHelper.cs │ ├── Key.cs │ ├── PCT.cs │ ├── TypeChildRelationship.cs │ └── Validation.cs ├── Pooling.cs ├── Properties │ └── PublishProfiles │ │ └── FolderProfile.pubxml ├── Thumbs.db ├── xSkrape.ico └── xSkrapeIcon.jpg ├── CodexMicroORM.SQLServer ├── CodexMicroORM.SQLServer.csproj └── DataAccess │ ├── MSSQLCommand.cs │ ├── MSSQLConnection.cs │ └── MSSQLProcBasedProvider.cs ├── CodexMicroORM.sln ├── LICENSE └── README.md /.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 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ "master" ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ "master" ] 20 | schedule: 21 | - cron: '26 10 * * 3' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'csharp' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 38 | 39 | steps: 40 | - name: Checkout repository 41 | uses: actions/checkout@v3 42 | 43 | # Initializes the CodeQL tools for scanning. 44 | - name: Initialize CodeQL 45 | uses: github/codeql-action/init@v2 46 | with: 47 | languages: ${{ matrix.language }} 48 | # If you wish to specify custom queries, you can do so here or in a config file. 49 | # By default, queries listed here will override any specified in a config file. 50 | # Prefix the list here with "+" to use these queries and those in the config file. 51 | 52 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 53 | # queries: security-extended,security-and-quality 54 | 55 | 56 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 57 | # If this step fails, then you should remove it and run the build manually (see below) 58 | - name: Autobuild 59 | uses: github/codeql-action/autobuild@v2 60 | 61 | # ℹ️ Command-line programs to run using the OS shell. 62 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 63 | 64 | # If the Autobuild fails above, remove it and uncomment the following three lines. 65 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 66 | 67 | # - run: | 68 | # echo "Run, Build Application using script" 69 | # ./location_of_script_within_repo/buildscript.sh 70 | 71 | - name: Perform CodeQL Analysis 72 | uses: github/codeql-action/analyze@v2 73 | -------------------------------------------------------------------------------- /.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 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 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 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc 262 | /.vs_ 263 | /CodexMicroORM.Core/.vs_ 264 | /CodexMicroORM.Core/CodexMicroORM.Core - Copy.csproj 265 | -------------------------------------------------------------------------------- /BaseTests/CodexMicroORM.BaseTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net9.0 5 | 6 | false 7 | 8 | 9 | 10 | 1701;1702;IDE0008;IDE0058;IDE0047;IDE0060;VSSpell001 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | PreserveNewest 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | all 29 | runtime; build; native; contentfiles; analyzers; buildtransitive 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | PreserveNewest 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /BaseTests/DBExtensions.CodeGen.cs: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Copyright 2021 CodeX Enterprises LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | Major Changes: 17 | 12/2017 0.2 Initial release (Joel Champagne) 18 | ***********************************************************************/ 19 | using System; 20 | using System.Data; 21 | using System.Threading.Tasks; 22 | using CodexMicroORM.Core; 23 | using CodexMicroORM.Core.Services; 24 | 25 | namespace CodexMicroORM.DemoObjects 26 | { 27 | public class PhoneSet : EntitySet 28 | { 29 | public void RetrieveAllForFamily(int ParentPersonID) 30 | { 31 | this.DBRetrieveByQuery(CommandType.StoredProcedure, "CEFTest.up_Phone_AllForFamily", ParentPersonID); 32 | } 33 | } 34 | 35 | /// 36 | /// The intent of this class is to illustrate what *should* be code generated based on the shape of the result sets from any custom stored procedures in a given database source. 37 | /// Doing so means we get compile-time errors if signatures change, which is good! 38 | /// We can also build an automated testing fascade based on these to ensure all procedures remain "unbroken". 39 | /// 40 | public static class GeneratedExtensions 41 | { 42 | public static EntitySet DBRetrieveAllForFamily(this EntitySet set, int ParentPersonID) 43 | { 44 | return set.DBRetrieveByQuery(CommandType.StoredProcedure, "CEFTest.up_Phone_AllForFamily", ParentPersonID, false); 45 | } 46 | public static EntitySet DBAppendAllForFamily(this EntitySet set, int ParentPersonID) 47 | { 48 | return set.DBAppendByQuery(CommandType.StoredProcedure, "CEFTest.up_Phone_AllForFamily", ParentPersonID, false); 49 | } 50 | public static EntitySet DBRetrieveByOwner(this EntitySet set, int PersonID, PhoneType? PhoneTypeID) 51 | { 52 | return set.DBRetrieveByQuery(CommandType.StoredProcedure, "CEFTest.up_Phone_ByPersonID", PersonID, PhoneTypeID); 53 | } 54 | public static EntitySet DBAppendByOwner(this EntitySet set, int PersonID, PhoneType? PhoneTypeID) 55 | { 56 | return set.DBAppendByQuery(CommandType.StoredProcedure, "CEFTest.up_Phone_ByPersonID", PersonID, PhoneTypeID); 57 | } 58 | 59 | public static EntitySet DBRetrieveByParentID(this EntitySet set, int ParentPersonID) 60 | { 61 | return set.DBRetrieveByQuery(CommandType.StoredProcedure, "CEFTest.up_Person_ByParentPersonID", ParentPersonID); 62 | } 63 | public static EntitySet DBAppendByParentID(this EntitySet set, int ParentPersonID) 64 | { 65 | return set.DBAppendByQuery(CommandType.StoredProcedure, "CEFTest.up_Person_ByParentPersonID", ParentPersonID); 66 | } 67 | public static EntitySet DBRetrieveSummaryForParents(this EntitySet set, int? MinimumAge) 68 | { 69 | return set.DBRetrieveByQuery(CommandType.StoredProcedure, "CEFTest.up_Person_SummaryForParents", MinimumAge); 70 | } 71 | public static EntitySet DBAppendSummaryForParents(this EntitySet set, int? MinimumAge) 72 | { 73 | return set.DBAppendByQuery(CommandType.StoredProcedure, "CEFTest.up_Person_SummaryForParents", MinimumAge); 74 | } 75 | 76 | public static EntitySet DBRetrieveByParentID(this EntitySet set, int ParentPersonID) 77 | { 78 | return set.DBRetrieveByQuery(CommandType.StoredProcedure, "CEFTest.up_Person_ByParentPersonID", ParentPersonID); 79 | } 80 | public static EntitySet DBAppendByParentID(this EntitySet set, int ParentPersonID) 81 | { 82 | return set.DBAppendByQuery(CommandType.StoredProcedure, "CEFTest.up_Person_ByParentPersonID", ParentPersonID); 83 | } 84 | public static EntitySet DBRetrieveSummaryForParents(this EntitySet set, int? MinimumAge) 85 | { 86 | return set.DBRetrieveByQuery(CommandType.StoredProcedure, "CEFTest.up_Person_SummaryForParents", MinimumAge); 87 | } 88 | public static EntitySet DBAppendSummaryForParents(this EntitySet set, int? MinimumAge) 89 | { 90 | return set.DBAppendByQuery(CommandType.StoredProcedure, "CEFTest.up_Person_SummaryForParents", MinimumAge); 91 | } 92 | 93 | public static EntitySet DBRetrieveByReceiptNumber(this EntitySet set, string ReceiptNumber) 94 | { 95 | return set.DBRetrieveByQuery(CommandType.StoredProcedure, "WTest.up_Receipt_ByReceiptNumber", ReceiptNumber); 96 | } 97 | public static EntitySet DBAppendByReceiptNumber(this EntitySet set, string ReceiptNumber) 98 | { 99 | return set.DBAppendByQuery(CommandType.StoredProcedure, "WTest.up_Receipt_ByReceiptNumber", ReceiptNumber); 100 | } 101 | 102 | public static EntitySet DBRetrieveByGroupNumber(this EntitySet set, string GroupNumber, DateTime? ReviewsSince) 103 | { 104 | return set.DBRetrieveByQuery(CommandType.StoredProcedure, "WTest.up_WidgetGroupItem_Items", GroupNumber, ReviewsSince); 105 | } 106 | public static EntitySet DBAppendByGroupNumber(this EntitySet set, string GroupNumber, DateTime? ReviewsSince) 107 | { 108 | return set.DBAppendByQuery(CommandType.StoredProcedure, "WTest.up_WidgetGroupItem_Items", GroupNumber, ReviewsSince); 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /BaseTests/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | // This file is used by Code Analysis to maintain SuppressMessage 2 | // attributes that are applied to this project. 3 | // Project-level suppressions either have no target or are given 4 | // a specific target and scoped to a namespace, type, member, etc. 5 | 6 | using System.Diagnostics.CodeAnalysis; 7 | 8 | [assembly: SuppressMessage("Usage", "CA2235:Mark all non-serializable fields", Justification = "Accepted", Scope = "module")] 9 | -------------------------------------------------------------------------------- /BaseTests/Modern12.cs: -------------------------------------------------------------------------------- 1 | using CodexMicroORM.Core; 2 | using CodexMicroORM.Core.Services; 3 | using CodexMicroORM.DemoObjects2; 4 | using CodexMicroORM.Providers; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Threading.Tasks; 10 | 11 | namespace CodexMicroORM.BaseTests 12 | { 13 | [TestClass] 14 | public class Modern12 15 | { 16 | private const string DB_SERVER = @"(local)\sql2016"; 17 | 18 | /// 19 | /// Covers classes created from latest code gen templates and most common settings have used lately. 20 | /// Please note: these tests should be run independently of those found in DBOps.cs - global settings may conflict. 21 | /// 22 | public Modern12() 23 | { 24 | Globals.ConnectionScopePerThread = false; 25 | Globals.UseTransactionsForNewScopes = false; 26 | Globals.UseAsyncSave = false; 27 | Globals.ResolveForArbitraryLoadOrder = true; 28 | Globals.DoCopyParseProperties = false; 29 | Globals.AddGlobalPropertyExcludedFromDirtyCheck("LastUpdatedDate"); 30 | Globals.AddGlobalPropertyExcludedFromDirtyCheck("LastUpdatedBy"); 31 | Globals.DefaultCacheBehavior = CacheBehavior.Off; 32 | Globals.DefaultRetrievalPostProcessing = RetrievalPostProcessing.PropertyGroups | RetrievalPostProcessing.PropertyNameFixups; 33 | MSSQLProcBasedProvider.SaveRetryCount = 3; 34 | MSSQLProcBasedProvider.OpenRetryCount = 3; 35 | Globals.CommandTimeoutSeconds = 120; 36 | 37 | CEF.AddGlobalService(DBService.Create(new MSSQLProcBasedProvider($@"Data Source={DB_SERVER};Database=CodexMicroORMTest;Integrated Security=SSPI;MultipleActiveResultSets=true;TrustServerCertificate=true", defaultSchema: "CEFTest"))); 38 | CEF.AddGlobalService(new AuditService(() => 39 | { 40 | return "test"; 41 | })); 42 | CEF.AddGlobalService(new MemoryFileSystemBacked(null, MemoryFileSystemBacked.CacheStorageStrategy.OnlyMemory)); 43 | 44 | // Added 1.2 - test methodology from latest code gen templates 45 | AttributeInitializer.Apply(typeof(Person).Assembly); 46 | } 47 | 48 | [TestMethod] 49 | public void DupRetrievalKeyPropogation() 50 | { 51 | using var ss = CEF.NewServiceScope(); 52 | var p = CEF.NewObject(); 53 | p.Age = 55; 54 | p.Name = "John"; 55 | p.Gender = "M"; 56 | Assert.AreEqual(1, CEF.DBSave().Count()); 57 | var pc = CEF.NewObject(); 58 | pc.ParentPersonID = p.PersonID; 59 | pc.Age = 35; 60 | pc.Name = "Jane"; 61 | pc.Gender = "F"; 62 | var ph = CEF.NewObject(); 63 | ph.PersonID = pc.AsDynamic().PersonID; // why? since UseShadowPropertiesForNew = true, need to get generated ID 64 | ph.Number = "555-1111"; 65 | ph.PhoneTypeID = 1; 66 | Assert.AreEqual(2, CEF.DBSave().Count()); 67 | 68 | PhoneSet phset = []; 69 | try 70 | { 71 | ss.SetRetrievalIdentityForObject(phset, RetrievalIdentityMode.ThrowErrorOnDuplicate); 72 | phset.RetrieveForFamily(p.PersonID, true); 73 | Assert.Fail("Should have thrown"); 74 | } 75 | catch (Exception ex) 76 | { 77 | Assert.IsTrue(ex.Message.Contains("Duplicate record found")); 78 | } 79 | 80 | phset.AllowRetrievalDups().RetrieveForFamily(p.PersonID, true); 81 | Assert.AreEqual(2, phset.Count); 82 | } 83 | 84 | [TestMethod] 85 | public void CreatingLoadingSaving() 86 | { 87 | using var ss = CEF.NewServiceScope(); 88 | var p = CEF.NewObject(); 89 | p.Age = 55; 90 | p.Name = "John"; 91 | p.Gender = "M"; 92 | Assert.AreEqual(1, CEF.DBSave().Count()); 93 | var ph = CEF.NewObject(); 94 | ph.PersonID = p.PersonID; 95 | ph.Number = "555-1212"; 96 | ph.PhoneTypeID = 1; 97 | Assert.AreEqual(1, CEF.DBSave().Count()); 98 | ph.PhoneTypeID = 2; 99 | Assert.AreEqual(1, CEF.DBSave().Count()); 100 | var ps = PersonSet.RetrieveByKey(p.PersonID); 101 | Assert.AreEqual(1, ps.Count); 102 | Assert.AreEqual("test", ps.First().LastUpdatedBy); 103 | CEF.DeleteObject(p); 104 | Assert.AreEqual(2, CEF.DBSave().Count()); 105 | var phs = PhoneSet.RetrieveByPersonID(p.PersonID, null); 106 | Assert.AreEqual(0, phs.Count); 107 | } 108 | 109 | [TestMethod] 110 | public void TestIndexedSetPreformance() 111 | { 112 | using var ss = CEF.NewServiceScope(); 113 | IndexedSet iset = new(); 114 | EntitySet eset = new(); 115 | 116 | for (int i = 1; i < 100000; i++) 117 | { 118 | var p = iset.Add(); 119 | p.Age = (i % 80) + 10; 120 | p.Name = $"John{i}"; 121 | 122 | var p2 = eset.Add(); 123 | p2.Age = (i % 80) + 10; 124 | p2.Name = $"John{i}"; 125 | } 126 | 127 | DateTime starte = DateTime.Now; 128 | long ecnt = 0; 129 | for (int j = 0; j < 1000; j++) 130 | { 131 | ecnt += (from a in eset where a.Age >= (j % 70) + 10 select a).Count(); 132 | } 133 | double edur = DateTime.Now.Subtract(starte).TotalMicroseconds; 134 | 135 | long icnt = 0; 136 | DateTime starti = DateTime.Now; 137 | for (int j = 0; j < 1000; j++) 138 | { 139 | icnt += (from a in iset where a.Age >= (j % 70) + 10 select a).Count(); 140 | } 141 | double idur = DateTime.Now.Subtract(starti).TotalMicroseconds; 142 | 143 | Assert.AreEqual(57499985, ecnt); 144 | Assert.AreEqual(57499985, icnt); 145 | Assert.IsTrue(idur < edur * 0.1); // indexed set should be >= 10x faster 146 | } 147 | 148 | public class PoolTestItem 149 | { 150 | public Guid Guid { get; set; } = Guid.NewGuid(); 151 | } 152 | 153 | [TestMethod] 154 | public void PoolableItemTest1() 155 | { 156 | var items = new HashSet(); 157 | 158 | Task.WaitAll(Enumerable.Range(1, 3).Select(async a => 159 | { 160 | using var pi = new PoolableItemWrapper(() => new PoolTestItem()); 161 | items.Add(pi.Item.Guid); 162 | await Task.Delay(100); 163 | }).ToArray()); 164 | 165 | { 166 | var pi = new PoolableItemWrapper(() => new PoolTestItem()); 167 | items.Add(pi.Item.Guid); 168 | pi.Dispose(); 169 | } 170 | 171 | Assert.IsTrue(PoolableItemWrapper.CurrentPoolCount <= 3); 172 | Assert.IsTrue(items.Count <= 3); 173 | } 174 | 175 | 176 | [TestMethod] 177 | public void IndexedSetEqualsAndRangeAndLinq() 178 | { 179 | using var ss = CEF.NewServiceScope(); 180 | IndexedSet ps = new(); 181 | var p1 = ps.Add(); 182 | p1.Age = 55; 183 | p1.Name = "John"; 184 | var p2 = ps.Add(); 185 | p2.Age = 50; 186 | p2.Name = "Fred"; 187 | var p3 = ps.Add(); 188 | p3.Age = 45; 189 | p3.Name = "Will"; 190 | 191 | Assert.AreEqual(50, ps.FindByEquality("Name", "Fred").First().Age); 192 | Assert.AreEqual(2, (from a in ps where a.Age >= 50 select a).Count()); 193 | var vp = ps.GetType().GetProperty("View", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(ps) as IndexedSnapshot; 194 | Assert.AreEqual(2, vp.IndexCount); // confirm did really auto-create indexes 195 | } 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /BaseTests/POCO.cs: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Copyright 2021 CodeX Enterprises LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | Major Changes: 17 | 12/2017 0.2 Initial release (Joel Champagne) 18 | 01/2018 0.2.4 Next set of objects to test with (widgets) (Joel Champagne) 19 | ***********************************************************************/ 20 | using CodexMicroORM.Core; 21 | using CodexMicroORM.Core.Services; 22 | using System; 23 | using System.Collections.Generic; 24 | 25 | namespace CodexMicroORM.DemoObjects 26 | { 27 | //[EntityRelationships("Person|ParentPersonID")] 28 | [Serializable] 29 | public class Person 30 | { 31 | public int PersonID { get; set; } 32 | public string Name { get; set; } 33 | public int Age { get; set; } 34 | public string Gender { get; set; } 35 | public IList Kids { get; set; } 36 | public IList Phones { get; set; } 37 | 38 | public virtual string Display 39 | { 40 | get 41 | { 42 | return $"{PersonID} - {Name} - {Age} - {(string.IsNullOrEmpty(Gender) ? "?" : Gender == "M" ? "Male" : "Female")}"; 43 | } 44 | } 45 | 46 | public override string ToString() 47 | { 48 | return Display; 49 | } 50 | } 51 | 52 | public enum PhoneType 53 | { 54 | Home = 1, 55 | Work = 2, 56 | Mobile = 3 57 | } 58 | 59 | /// 60 | /// A phone usually has an owner but can be unowned (a single person). 61 | /// Look ma! No PhoneID! 62 | /// 63 | public class Phone 64 | { 65 | public Person Owner { get; set; } 66 | public PhoneType PhoneTypeID { get; set; } 67 | public string Number { get; set; } 68 | public DateTime? LastUpdatedDate { get; } 69 | 70 | public override string ToString() 71 | { 72 | return $"{Number} - {PhoneTypeID} - {(Owner == null ? "null" : Owner.Name)}"; 73 | } 74 | } 75 | 76 | // Next set of classes are for next set of use cases - field-level mapping demo and more sophisticated tests 77 | 78 | public class Customer 79 | { 80 | public Guid CustomerID { get; set; } 81 | public string Name { get; set; } 82 | 83 | public Address Address { get; set; } 84 | } 85 | 86 | public class Address 87 | { 88 | public string AddressLine { get; set; } 89 | 90 | public string City { get; set; } 91 | } 92 | 93 | public class WidgetStatus 94 | { 95 | public byte WidgetStatusID { get; set; } // In DB, it's "ID" 96 | public string StatusDesc { get; set; } 97 | public string StatusCode { get; set; } 98 | } 99 | 100 | public class WidgetType 101 | { 102 | public string SKU { get; set; } // PK 103 | public string Description { get; set; } 104 | } 105 | 106 | public class Widget 107 | { 108 | public int WidgetID { get; set; } 109 | public string SKU { get; set; } 110 | public string SerialNumber { get; set; } 111 | public WidgetStatusValues CurrentStatus { get; set; } // Use a default value (required field) 112 | public decimal? Cost { get; set; } 113 | public decimal? BilledAmount { get; set; } 114 | } 115 | 116 | public class Receipt : WidgetList 117 | { 118 | public string ReceiptNumber // Maps to a shared table's GroupNumber (we can leave this unmapped since it's simply a proxy to GroupNumber 119 | { 120 | get 121 | { 122 | return GroupNumber; 123 | } 124 | set 125 | { 126 | GroupNumber = value; 127 | } 128 | } 129 | 130 | public Customer ReceiptCustomer { get; set; } // Maps to a shared table's Customer 131 | 132 | public Address FromAddress { get; set; } 133 | public Address FinalDest { get; set; } 134 | } 135 | 136 | public class Shipment : WidgetList 137 | { 138 | public string ShipmentNumber // Maps to a shared table's GroupNumber (we can leave this unmapped since it's simply a proxy to GroupNumber 139 | { 140 | get 141 | { 142 | return GroupNumber; 143 | } 144 | set 145 | { 146 | GroupNumber = value; 147 | } 148 | } 149 | 150 | public Customer ShipmentCustomer { get; set; } // Maps to a shared table's Customer 151 | public Customer BillingCustomer { get; set; } 152 | 153 | public Address ViaAddress { get; set; } 154 | } 155 | 156 | public class WidgetList // Effective n:m between widgets and list (e.g. 1 widget can be part of both a receipt and a shipment, and a receipt or shipment can have many widgets) 157 | { 158 | public IList Widgets { get; set; } 159 | 160 | public string GroupNumber { get; set; } 161 | } 162 | 163 | /// 164 | /// Demonstrates a compound primary key, both attributes of which are strings 165 | /// 166 | public class WidgetReview 167 | { 168 | public string Username { get; set; } // PK1 169 | public WidgetType RatingFor { get; set; } // PK2 (SKU) 170 | public int Rating { get; set; } // tinyint in DB, range validator 171 | } 172 | 173 | public class GroupItem // In DB, PK is WidgetID + ShipmentID 174 | { 175 | public Widget Widget { get; set; } 176 | public string TrackingNumber { get; set; } 177 | } 178 | 179 | } 180 | -------------------------------------------------------------------------------- /BaseTests/SandboxTests.cs: -------------------------------------------------------------------------------- 1 | using CodexMicroORM.Core; 2 | using CodexMicroORM.Core.Services; 3 | using CodexMicroORM.DemoObjects; 4 | using CodexMicroORM.Providers; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | using System.Threading; 12 | 13 | namespace BaseTests 14 | { 15 | [TestClass] 16 | public class StandaloneTests 17 | { 18 | [TestMethod] 19 | public async Task ExecWithWaitAsync() 20 | { 21 | StringBuilder sb = new(); 22 | 23 | var d = async (CancellationToken ct) => 24 | { 25 | sb.Append("point 0;"); 26 | await Task.Delay(6000); 27 | sb.Append("point 1;"); 28 | ct.ThrowIfCancellationRequested(); 29 | sb.Append("point 2;"); 30 | }; 31 | 32 | var r1 = await d.ExecuteWithMaxWaitAsync(5000); 33 | sb.Append($"result {r1};"); 34 | sb = new(); 35 | 36 | var d2 = async (CancellationToken ct) => 37 | { 38 | for (int i = 1; i < 6; ++i) 39 | { 40 | await Task.Delay(1000, ct); 41 | sb.Append($"point2 {i};"); 42 | } 43 | }; 44 | 45 | var r2 = await d2.ExecuteWithMaxWaitAsync(5000); 46 | sb.Append($"result {r2};"); 47 | sb = new(); 48 | 49 | var d3 = (CancellationToken ct) => 50 | { 51 | sb.Append("point3a;"); 52 | System.Threading.Thread.Sleep(100); 53 | sb.Append("point3b;"); 54 | ct.ThrowIfCancellationRequested(); 55 | sb.Append("point3c;"); 56 | }; 57 | 58 | var r3 = await d3.ExecuteWithMaxWaitAsync(5000); 59 | sb.Append($"result {r3};"); 60 | sb = new(); 61 | 62 | int c = 0; 63 | 64 | var d4 = (CancellationToken ct) => 65 | { 66 | sb.Append($"point4 {c};"); 67 | ++c; 68 | ct.ThrowIfCancellationRequested(); 69 | if (c < 3) 70 | { 71 | throw new ApplicationException("some issue"); 72 | } 73 | System.Threading.Thread.Sleep(100); 74 | ct.ThrowIfCancellationRequested(); 75 | sb.Append("done;"); 76 | ct.ThrowIfCancellationRequested(); 77 | }; 78 | 79 | var r4 = await d4.ExecuteWithMaxWaitAsync(10000, true, null, 5); 80 | sb.Append($"result {r4};"); 81 | 82 | Console.WriteLine(sb.ToString()); 83 | } 84 | } 85 | 86 | public class SandboxTests : MarshalByRefObject 87 | { 88 | private const string DB_SERVER = @"(local)\sql2016"; 89 | 90 | public SandboxTests() 91 | { 92 | Globals.WrapperSupports = WrappingSupport.Notifications; 93 | Globals.WrappingClassNamespace = null; 94 | Globals.WrapperClassNamePattern = "{0}Wrapped"; 95 | CEF.AddGlobalService(DBService.Create(new MSSQLProcBasedProvider($@"Data Source={DB_SERVER};Database=CodexMicroORMTest;Integrated Security=SSPI;MultipleActiveResultSets=true", defaultSchema: "CEFTest"))); 96 | CEF.AddGlobalService(new AuditService()); 97 | 98 | KeyService.RegisterKey(nameof(Person.PersonID)); 99 | KeyService.RegisterKey("PhoneID"); 100 | 101 | KeyService.RegisterRelationship(TypeChildRelationship.Create("ParentPersonID").MapsToChildProperty(nameof(Person.Kids))); 102 | KeyService.RegisterRelationship(TypeChildRelationship.Create().MapsToParentProperty(nameof(Phone.Owner)).MapsToChildProperty(nameof(Person.Phones))); 103 | } 104 | 105 | public string CaseInsensitivePropAccessAndTLSGlobals() 106 | { 107 | try 108 | { 109 | Globals.CaseSensitiveDictionaries = false; 110 | 111 | using (CEF.NewServiceScope()) 112 | { 113 | var p = CEF.NewObject(new Person() { Name = "Test1", Age = 11, Gender = "M" }); 114 | Assert.AreEqual(CEF.DBSave().Count(), 1); 115 | var ps = new EntitySet().DBRetrieveByKey(p.PersonID); 116 | var p2 = ps.First().AsInfraWrapped(); 117 | p2.SetValue("myArbitrary", 123, typeof(int)); 118 | Assert.AreEqual("Test1", p2.AsDynamic().name); 119 | Assert.AreEqual(123, p2.AsDynamic().Myarbitrary); 120 | return null; 121 | } 122 | } 123 | catch (Exception ex) 124 | { 125 | return ex.Message; 126 | } 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /BaseTests/Wrappers.CodeGen.cs: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Copyright 2021 CodeX Enterprises LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | Major Changes: 17 | 12/2017 0.2 Initial release (Joel Champagne) 18 | ***********************************************************************/ 19 | using CodexMicroORM.Core; 20 | using CodexMicroORM.Core.Services; 21 | using System; 22 | using System.Collections.Generic; 23 | using System.ComponentModel; 24 | 25 | namespace CodexMicroORM.DemoObjects 26 | { 27 | public enum WidgetStatusValues 28 | { 29 | PEND = 1, 30 | RECV = 2, 31 | PROC = 3, 32 | SHIP = 4 33 | } 34 | 35 | /// 36 | /// Keep in mind: the intent of this class is to be CODE GENERATED - you will not need to write this!!! 37 | /// Ideally it would be sourced from class metadata (Person) and the underlying database store. 38 | /// This compliments the startup code which should also be code generated based on DB schema and/or logical model definition. 39 | /// 40 | [Serializable] 41 | public class PersonWrapped : Person, INotifyPropertyChanged, ICEFWrapper 42 | { 43 | #region "ICEFWrapper-specific" 44 | 45 | private Person _copyTo = null; 46 | 47 | Type ICEFWrapper.GetBaseType() 48 | { 49 | return typeof(Person); 50 | } 51 | 52 | string ICEFWrapper.GetSchemaName() 53 | { 54 | return "CEFTest"; 55 | } 56 | 57 | void ICEFWrapper.SetCopyTo(object wrapped) 58 | { 59 | _copyTo = wrapped as Person; 60 | } 61 | 62 | object ICEFWrapper.GetCopyTo() 63 | { 64 | return _copyTo; 65 | } 66 | 67 | #endregion 68 | 69 | // Generated - initialize known collections 70 | // Note: changed in 1.2 from List to EntitySet for Kids to acknowledge need for change tracking, null initialization, etc. 71 | public PersonWrapped() 72 | { 73 | Kids = new EntitySet(); 74 | Phones = new EntitySet(); 75 | } 76 | 77 | public new IList Kids 78 | { 79 | get 80 | { 81 | return base.Kids; 82 | } 83 | set 84 | { 85 | bool changed = (Kids != value); 86 | base.Kids = value; 87 | if (_copyTo != null) 88 | { 89 | _copyTo.Kids = value; 90 | } 91 | if (changed) 92 | { 93 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Kids))); 94 | } 95 | } 96 | } 97 | 98 | public new int PersonID 99 | { 100 | get 101 | { 102 | return base.PersonID; 103 | } 104 | set 105 | { 106 | bool changed = (PersonID != value); 107 | base.PersonID = value; 108 | if (_copyTo != null) 109 | { 110 | _copyTo.PersonID = value; 111 | } 112 | if (changed) 113 | { 114 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(PersonID))); 115 | } 116 | } 117 | } 118 | 119 | public new string Name 120 | { 121 | get 122 | { 123 | return base.Name; 124 | } 125 | set 126 | { 127 | bool changed = (base.Name != value); 128 | base.Name = value; 129 | if (_copyTo != null) 130 | { 131 | _copyTo.Name = value; 132 | } 133 | if (changed) 134 | { 135 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Name))); 136 | } 137 | } 138 | } 139 | 140 | public new int Age 141 | { 142 | get 143 | { 144 | return base.Age; 145 | } 146 | set 147 | { 148 | bool changed = (base.Age != value); 149 | base.Age = value; 150 | if (_copyTo != null) 151 | { 152 | _copyTo.Age = value; 153 | } 154 | if (changed) 155 | { 156 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Age))); 157 | } 158 | } 159 | } 160 | 161 | private int? _ParentPersonID; 162 | public int? ParentPersonID 163 | { 164 | get 165 | { 166 | return _ParentPersonID; 167 | } 168 | set 169 | { 170 | bool changed = (_ParentPersonID != value); 171 | _ParentPersonID = value; 172 | if (changed) 173 | { 174 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ParentPersonID))); 175 | } 176 | } 177 | } 178 | 179 | private string _LastUpdatedBy; 180 | public string LastUpdatedBy 181 | { 182 | get 183 | { 184 | return _LastUpdatedBy; 185 | } 186 | set 187 | { 188 | bool changed = (_LastUpdatedBy != value); 189 | _LastUpdatedBy = value; 190 | if (changed) 191 | { 192 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(LastUpdatedBy))); 193 | } 194 | } 195 | } 196 | 197 | private DateTime _LastUpdatedDate; 198 | public DateTime LastUpdatedDate 199 | { 200 | get 201 | { 202 | return _LastUpdatedDate; 203 | } 204 | set 205 | { 206 | bool changed = (_LastUpdatedDate != value); 207 | _LastUpdatedDate = value; 208 | if (changed) 209 | { 210 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(LastUpdatedDate))); 211 | } 212 | } 213 | } 214 | 215 | public event PropertyChangedEventHandler PropertyChanged; 216 | } 217 | 218 | } 219 | -------------------------------------------------------------------------------- /CodexMicroORM.BindingSupport/CodexMicroORM.BindingSupport.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {9698510F-6393-43A8-9D71-504FAC6A5332} 8 | Library 9 | Properties 10 | CodexMicroORM.BindingSupport 11 | CodexMicroORM.BindingSupport 12 | v4.7.2 13 | 512 14 | 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | xSkrape.ico 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | {0be418f4-b416-4945-b664-b052b9bf4947} 61 | CodexMicroORM.Core 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /CodexMicroORM.BindingSupport/CodexMicroORM.BindingSupport.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CodexMicroORM.BindingSupport 5 | 0.9.0 6 | CodexMicroORM UI Binding Support 7 | CodeX Enterprises LLC 8 | CodeX Enterprises LLC 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | https://github.com/codexguy/CodexMicroORM 11 | http://www.xskrape.com/images/cef64.png 12 | true 13 | An alternative to ORM's such as Entity Framework, offers light-weight database mapping to your existing CLR objects with minimal effort. BindingSupport is net461, supporting two-way data-binding with entities. 14 | Visit on GitHub for details. 15 | Copyright (c) 2019, All Rights Reserved 16 | ORM microORM entity-framework database object-mapping micro-ORM entity data-access dataset linq-to-sql data-binding wpf 17 | 18 | 19 | -------------------------------------------------------------------------------- /CodexMicroORM.BindingSupport/Collections.cs: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Copyright 2018 CodeX Enterprises LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | Major Changes: 17 | 12/2017 0.2 Initial release (Joel Champagne) 18 | ***********************************************************************/ 19 | using CodexMicroORM.Core; 20 | using CodexMicroORM.Core.Services; 21 | using System; 22 | using System.Collections.Generic; 23 | using System.Collections.ObjectModel; 24 | using System.Collections.Specialized; 25 | using System.ComponentModel; 26 | using System.Linq; 27 | 28 | namespace CodexMicroORM.BindingSupport 29 | { 30 | /// 31 | /// A GenericBindableSet is an ObservableCollection of DynamicBindable. It resembles a DataTable, as such, since it offers no strong-typed CLR properties to access data. 32 | /// It's flexibility is in that it can be bound to WPF lists easily, as DynamicBindable's implement ICustomTypeProvider. 33 | /// 34 | public class GenericBindableSet : BindingList, IDisposable, ITypedList 35 | { 36 | private bool _isDirty = false; 37 | 38 | public event EventHandler DirtyStateChange; 39 | public event EventHandler RowPropertyChanged; 40 | 41 | public Dictionary ExternalSchema 42 | { 43 | get; 44 | set; 45 | } = new Dictionary(); 46 | 47 | public ServiceScope OwningScope 48 | { 49 | get; 50 | set; 51 | } = CEF.CurrentServiceScope; 52 | 53 | public Type BaseItemType 54 | { 55 | get; 56 | set; 57 | } 58 | 59 | public bool ScanClean 60 | { 61 | get; 62 | set; 63 | } = false; 64 | 65 | internal GenericBindableSet(IEnumerable source) : base(source.ToList()) 66 | { 67 | AddTracking(source); 68 | this.AllowNew = true; 69 | this.AllowEdit = true; 70 | this.AllowRemove = true; 71 | } 72 | 73 | private void ItemPropertyChanged(object sender, PropertyChangedEventArgs e) 74 | { 75 | SetDirty(); 76 | RowPropertyChanged?.Invoke(sender, e); 77 | } 78 | 79 | public bool IsValid 80 | { 81 | get 82 | { 83 | foreach (var r in this) 84 | { 85 | var ide = r.Wrapped as IDataErrorInfo; 86 | 87 | if (ide != null && !string.IsNullOrEmpty(ide.Error)) 88 | { 89 | return false; 90 | } 91 | } 92 | 93 | return true; 94 | } 95 | } 96 | 97 | public bool IsDirty => _isDirty; 98 | 99 | protected override object AddNewCore() 100 | { 101 | using (CEF.UseServiceScope(OwningScope)) 102 | { 103 | // We rely on construction of a new DynamicBindable that has the same shape as the first item in the collection, if any exist 104 | if (this.Any()) 105 | { 106 | var f = this.First(); 107 | var wot = f.Wrapped?.GetWrappedObject()?.GetType(); 108 | 109 | if (wot != null) 110 | { 111 | var no = Activator.CreateInstance(wot); 112 | var wno = CEF.IncludeObject(no, ObjectState.Added); 113 | var nod = wno.AsDynamicBindable(); 114 | base.Add(nod); 115 | return nod; 116 | } 117 | } 118 | 119 | // If none exist, need to rely on the "default schema" provided 120 | if (BaseItemType != null) 121 | { 122 | var no = Activator.CreateInstance(BaseItemType); 123 | var wno = CEF.IncludeObject(no, ObjectState.Added); 124 | var iw = wno.AsInfraWrapped(); 125 | var nod = wno.AsDynamicBindable(); 126 | 127 | if (ExternalSchema != null) 128 | { 129 | foreach (var e in ExternalSchema) 130 | { 131 | if (!iw.HasProperty(e.Key)) 132 | { 133 | iw.SetValue(e.Key, null, e.Value); 134 | } 135 | } 136 | } 137 | 138 | base.Add(nod); 139 | return nod; 140 | } 141 | 142 | // No default schema? It's an error situation. 143 | throw new InvalidOperationException("Cannot add a new item to the GenericBindableSet collection since there's no object definition available."); 144 | } 145 | } 146 | 147 | public void ResetClean() 148 | { 149 | if (_isDirty) 150 | { 151 | DirtyStateChange?.Invoke(this, new DirtyStateChangeEventArgs(false)); 152 | _isDirty = false; 153 | } 154 | } 155 | 156 | internal void SetDirty() 157 | { 158 | if (!_isDirty) 159 | { 160 | DirtyStateChange?.Invoke(this, new DirtyStateChangeEventArgs(true)); 161 | _isDirty = true; 162 | } 163 | } 164 | 165 | private void AddTracking(IEnumerable toAdd) 166 | { 167 | foreach (var s in toAdd) 168 | { 169 | s.PropertyChanged += ItemPropertyChanged; 170 | s.DirtyStateChange += S_DirtyStateChange; 171 | } 172 | } 173 | 174 | private void S_DirtyStateChange(object sender, Core.Services.DirtyStateChangeEventArgs e) 175 | { 176 | if (ScanClean && e.NewState == Core.ObjectState.Unchanged) 177 | { 178 | if (!(from a in this where a.State != Core.ObjectState.Unchanged select a).Any()) 179 | { 180 | ResetClean(); 181 | } 182 | } 183 | } 184 | 185 | private void RemoveTracking(IEnumerable toRemove) 186 | { 187 | foreach (var s in toRemove) 188 | { 189 | s.PropertyChanged -= ItemPropertyChanged; 190 | s.DirtyStateChange -= S_DirtyStateChange; 191 | } 192 | } 193 | 194 | protected override void InsertItem(int index, DynamicBindable item) 195 | { 196 | base.InsertItem(index, item); 197 | AddTracking(new DynamicBindable[] { item }); 198 | SetDirty(); 199 | } 200 | 201 | protected override void RemoveItem(int index) 202 | { 203 | var i = this[index]; 204 | RemoveTracking(new DynamicBindable[] { this[index] }); 205 | base.RemoveItem(index); 206 | 207 | using (CEF.UseServiceScope(OwningScope)) 208 | { 209 | var uw = i.Wrapped; 210 | 211 | if (uw != null) 212 | { 213 | CEF.DeleteObject(uw); 214 | } 215 | } 216 | 217 | SetDirty(); 218 | } 219 | 220 | protected override void SetItem(int index, DynamicBindable item) 221 | { 222 | bool change = (item != this[index]); 223 | RemoveTracking(new DynamicBindable[] { this[index] }); 224 | base.SetItem(index, item); 225 | AddTracking(new DynamicBindable[] { item }); 226 | 227 | if (change) 228 | { 229 | SetDirty(); 230 | } 231 | } 232 | 233 | protected override void ClearItems() 234 | { 235 | RemoveTracking(this); 236 | base.ClearItems(); 237 | SetDirty(); 238 | } 239 | 240 | public class DirtyStateChangeEventArgs : EventArgs 241 | { 242 | private bool _newState; 243 | 244 | internal DirtyStateChangeEventArgs(bool newState) 245 | { 246 | _newState = newState; 247 | } 248 | 249 | public bool IsDirty => _newState; 250 | } 251 | 252 | #region IDisposable Support 253 | private bool _disposedValue = false; // To detect redundant calls 254 | 255 | protected virtual void Dispose(bool disposing) 256 | { 257 | if (!_disposedValue) 258 | { 259 | if (disposing) 260 | { 261 | RemoveTracking(this); 262 | } 263 | 264 | _disposedValue = true; 265 | } 266 | } 267 | 268 | public void Dispose() 269 | { 270 | Dispose(true); 271 | } 272 | #endregion 273 | 274 | public string GetListName(PropertyDescriptor[] listAccessors) 275 | { 276 | return typeof(GenericBindableSet).Name; 277 | } 278 | 279 | public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors) 280 | { 281 | PropertyDescriptorCollection pdc; 282 | 283 | if (this.Count > 0) 284 | { 285 | // If we have data, use the underlying data 286 | var i = this[0]; 287 | pdc = ((ICustomTypeDescriptor)i).GetProperties(); 288 | } 289 | else 290 | { 291 | pdc = new PropertyDescriptorCollection(null); 292 | } 293 | 294 | // No data, rely on external schema if available 295 | foreach (var s in ExternalSchema) 296 | { 297 | pdc.Add(DynamicBindable.GetNewPropertyDescriptor(s.Key, s.Value)); 298 | } 299 | 300 | return pdc; 301 | } 302 | } 303 | } 304 | -------------------------------------------------------------------------------- /CodexMicroORM.BindingSupport/Extensions.cs: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Copyright 2018 CodeX Enterprises LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | Major Changes: 17 | 12/2017 0.2 Initial release (Joel Champagne) 18 | ***********************************************************************/ 19 | using System; 20 | using System.Collections.Generic; 21 | using System.Linq; 22 | using CodexMicroORM.Core; 23 | using CodexMicroORM.Core.Services; 24 | 25 | namespace CodexMicroORM.BindingSupport 26 | { 27 | public static class Extensions 28 | { 29 | public static GenericBindableSet AsDynamicBindable(this IEnumerable list) where T : ICEFInfraWrapper, new() 30 | { 31 | return new GenericBindableSet(from a in list let d = a.AsInfraWrapped() as DynamicWithBag where d != null select new DynamicBindable(d)) 32 | { 33 | BaseItemType = typeof(T) 34 | }; 35 | } 36 | 37 | public static GenericBindableSet AsDynamicBindable(this System.Collections.IEnumerable list) 38 | { 39 | return new GenericBindableSet(from a in list.Cast() let d = a.AsInfraWrapped() as DynamicWithBag where d != null select new DynamicBindable(d)); 40 | } 41 | 42 | public static GenericBindableSet AsDynamicBindable(this EntitySet list) where T : class, new() 43 | { 44 | return new GenericBindableSet(from a in list let d = a.AsInfraWrapped() as DynamicWithBag where d != null select new DynamicBindable(d)) 45 | { 46 | ExternalSchema = list.ExternalSchema, 47 | BaseItemType = typeof(T) 48 | }; 49 | } 50 | 51 | public static DynamicBindable AsDynamicBindable(this object o) 52 | { 53 | var d = (o.AsInfraWrapped() as DynamicWithBag) ?? throw new ArgumentException("Type does not have an infrastructure wrapper."); 54 | return new DynamicBindable(d); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /CodexMicroORM.BindingSupport/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("CodexMicroORM.BindingSupport")] 9 | [assembly: AssemblyDescription("Provides data binding services for UI's for CodexMicroORM")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("CodeX Enterprises LLC")] 12 | [assembly: AssemblyProduct("CodexMicroORM.BindingSupport")] 13 | [assembly: AssemblyCopyright("Copyright © 2019")] 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("9698510f-6393-43a8-9d71-504fac6a5332")] 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("0.9.0.0")] 36 | [assembly: AssemblyFileVersion("0.9.0.0")] 37 | -------------------------------------------------------------------------------- /CodexMicroORM.BindingSupport/xSkrape.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codexguy/CodexMicroORM/ebaa8bc6c8fe4cfabe460329f0a95ddb4644119e/CodexMicroORM.BindingSupport/xSkrape.ico -------------------------------------------------------------------------------- /CodexMicroORM.CodexV1CompatLayer/CodexMicroORM.CodexV1CompatLayer/CodexMicroORM.CodexV1CompatLayer.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0;net462;net8.0;net9.0 5 | 1.2.0 6 | true 7 | CodeX Enterprises LLC 8 | CodeX Enterprises LLC 9 | 10 | Copyright (c) 2024, All Rights Reserved 11 | true 12 | 13 | See: https://github.com/codexguy/CodexMicroORM 14 | For users of CodexFramework V1.0, this package offers a way to change less code when migrating to use CodexMicroORM as a replacement framework. 15 | https://github.com/codexguy/CodexMicroORM 16 | ORM microORM entity-framework database object-mapping micro-ORM entity data-access dataset linq-to-sql 17 | 1.2.0.0 18 | Apache-2.0 19 | xSkrapeIcon.jpg 20 | latest 21 | 1.2.0.0 22 | 23 | 24 | 25 | 1701;1702;IDE0008;IDE0058;IDE0047;IDE0060;VSSpell001 26 | 27 | 28 | 1701;1702;IDE0008;IDE0058;IDE0047;IDE0060;VSSpell001 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | True 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /CodexMicroORM.CodexV1CompatLayer/CodexMicroORM.CodexV1CompatLayer/Extensions.cs: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Copyright 2024 CodeX Enterprises LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | Major Changes: 17 | 06/2018 0.7 Initial release (Joel Champagne) 18 | ***********************************************************************/ 19 | #nullable enable 20 | using CodexMicroORM.Core; 21 | using CodexMicroORM.Core.Services; 22 | using System; 23 | 24 | namespace CodeXFramework.BaseEntity 25 | { 26 | public static class Extensions 27 | { 28 | public static void Save(this EntitySet target) where T : class, new() 29 | { 30 | target.DBSave(); 31 | } 32 | public static void RetrieveByKey(this EntitySet target, params object[] args) where T : class, new() 33 | { 34 | target.DBRetrieveByKey(args); 35 | } 36 | 37 | public static DateTime? ConvertToTimezone(this DateTime? dt, string tzid) 38 | { 39 | if (dt.HasValue && !string.IsNullOrEmpty(tzid)) 40 | { 41 | if (dt.Value.Kind == DateTimeKind.Local) 42 | { 43 | dt = dt.Value.ToUniversalTime(); 44 | } 45 | 46 | var tzi = TimeZoneInfo.FindSystemTimeZoneById(tzid); 47 | 48 | if (tzi != null) 49 | { 50 | return TimeZoneInfo.ConvertTimeFromUtc(dt.Value, tzi); 51 | } 52 | } 53 | 54 | return dt; 55 | } 56 | 57 | public static string Format(this DateTime? dt, string fmt) 58 | { 59 | if (dt.HasValue) 60 | return dt.Value.ToString(fmt); 61 | 62 | return string.Empty; 63 | } 64 | 65 | public static DateTime ConvertToTimezone(this DateTime dt, string tzid) 66 | { 67 | if (!string.IsNullOrEmpty(tzid)) 68 | { 69 | if (dt.Kind == DateTimeKind.Local) 70 | { 71 | dt = dt.ToUniversalTime(); 72 | } 73 | 74 | var tzi = TimeZoneInfo.FindSystemTimeZoneById(tzid); 75 | 76 | if (tzi != null) 77 | { 78 | return TimeZoneInfo.ConvertTimeFromUtc(dt, tzi); 79 | } 80 | } 81 | 82 | return dt; 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /CodexMicroORM.CodexV1CompatLayer/CodexMicroORM.CodexV1CompatLayer/GenericSet.cs: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Copyright 2024 CodeX Enterprises LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | Major Changes: 17 | 06/2018 0.7 Initial release (Joel Champagne) 18 | ***********************************************************************/ 19 | #nullable enable 20 | using CodexMicroORM.Core; 21 | using CodexMicroORM.Core.Services; 22 | using System; 23 | using System.Linq; 24 | 25 | namespace CodeXFramework.BaseEntity 26 | { 27 | public class GenericSet : EntitySet 28 | { 29 | public int RetrieveByQuery(string procName, params object?[] args) 30 | { 31 | this.DBRetrieveByQuery(procName, args); 32 | return this.Count; 33 | } 34 | 35 | public T GetItem(int rowNum, string fieldName) 36 | { 37 | if (rowNum >= this.Count) 38 | { 39 | throw new ArgumentOutOfRangeException("rowNum is larger than the colleciton size."); 40 | } 41 | 42 | var row = this.Skip(rowNum).FirstOrDefault(); 43 | 44 | if (row == null) 45 | { 46 | throw new InvalidOperationException("Could not find row."); 47 | } 48 | 49 | var iw = row.AsInfraWrapped(); 50 | 51 | if (iw == null) 52 | { 53 | throw new InvalidOperationException("Could not find wrapped row."); 54 | } 55 | 56 | if (Nullable.GetUnderlyingType(typeof(T)) != null) 57 | { 58 | return (T)Activator.CreateInstance(typeof(T), iw.GetValue(fieldName))!; 59 | } 60 | else 61 | { 62 | return (T)Convert.ChangeType(iw.GetValue(fieldName), typeof(T))!; 63 | } 64 | } 65 | } 66 | 67 | public class GenericSetRow 68 | { 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /CodexMicroORM.CodexV1CompatLayer/CodexMicroORM.CodexV1CompatLayer/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | // This file is used by Code Analysis to maintain SuppressMessage 2 | // attributes that are applied to this project. 3 | // Project-level suppressions either have no target or are given 4 | // a specific target and scoped to a namespace, type, member, etc. 5 | 6 | using System.Diagnostics.CodeAnalysis; 7 | 8 | [assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Code checked", Scope = "module")] 9 | [assembly: SuppressMessage("Usage", "CA2235:Mark all non-serializable fields", Justification = "Checked", Scope = "module")] 10 | -------------------------------------------------------------------------------- /CodexMicroORM.CodexV1CompatLayer/CodexMicroORM.CodexV1CompatLayer/ParallelWorkload.cs: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Copyright 2024 CodeX Enterprises LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | Major Changes: 17 | 06/2018 0.7 Initial release (Joel Champagne) 18 | ***********************************************************************/ 19 | #nullable enable 20 | using CodexMicroORM.Core; 21 | using System; 22 | using System.Collections; 23 | using System.Collections.Generic; 24 | using System.Linq; 25 | using System.Text; 26 | using System.Threading; 27 | using System.Threading.Tasks; 28 | 29 | namespace CodeXFramework.BaseEntity 30 | { 31 | public sealed class ParallelWorkload 32 | { 33 | #pragma warning disable IDE0060 // Remove unused parameter 34 | public static IterateResult RunUnorderedWorkload(IList src, int smalllist, int dop, Func body) 35 | #pragma warning restore IDE0060 // Remove unused parameter 36 | { 37 | int idx = 0; 38 | List errors = []; 39 | List data = []; 40 | 41 | if (src.Count <= smalllist) 42 | { 43 | foreach (var a in src) 44 | { 45 | try 46 | { 47 | var lidx = Interlocked.Add(ref idx, 1); 48 | var d = body.Invoke(a, lidx - 1, src.Count - lidx, []); 49 | 50 | lock (data) 51 | { 52 | data.Add(d); 53 | } 54 | } 55 | catch (Exception ex) 56 | { 57 | errors.Add(ex); 58 | } 59 | } 60 | } 61 | else 62 | { 63 | var ss = CEF.CurrentServiceScope; 64 | 65 | _ = Parallel.ForEach(src.Cast(), (a) => 66 | { 67 | try 68 | { 69 | using (CEF.UseServiceScope(ss)) 70 | { 71 | var lidx = Interlocked.Add(ref idx, 1); 72 | var d = body.Invoke(a, lidx - 1, src.Count - lidx, []); 73 | 74 | lock (data) 75 | { 76 | data.Add(d); 77 | } 78 | } 79 | } 80 | catch (Exception ex) 81 | { 82 | lock (errors) 83 | { 84 | errors.Add(ex); 85 | } 86 | } 87 | }); 88 | } 89 | 90 | return new IterateResult(data.ToArray(), errors.ToArray()); 91 | } 92 | 93 | public static void RunWorkloadFunctions(bool ordered, params Action[] args) 94 | { 95 | if (ordered) 96 | { 97 | foreach (var a in args) 98 | { 99 | a.Invoke(); 100 | } 101 | } 102 | else 103 | { 104 | var ss = CEF.CurrentServiceScope; 105 | 106 | Parallel.ForEach(args, (a) => 107 | { 108 | using (CEF.UseServiceScope(ss)) 109 | { 110 | a.Invoke(); 111 | } 112 | }); 113 | } 114 | } 115 | 116 | public static void RunWorkloadFunctions(params Action[] args) 117 | { 118 | var ss = CEF.CurrentServiceScope; 119 | 120 | Parallel.ForEach(args, (a) => 121 | { 122 | using (CEF.UseServiceScope(ss)) 123 | { 124 | a.Invoke(); 125 | } 126 | }); 127 | } 128 | } 129 | 130 | public sealed class IterateResult 131 | { 132 | private readonly object?[]? _data; 133 | private readonly Exception[] _errors; 134 | 135 | internal IterateResult(bool cancelled) 136 | { 137 | Cancelled = cancelled; 138 | _errors = []; 139 | } 140 | 141 | internal IterateResult(object[] data, Exception[] errors) 142 | { 143 | _data = data; 144 | _errors = errors; 145 | } 146 | 147 | public bool Cancelled { get; } = false; 148 | 149 | public IList GetData() where T : class 150 | { 151 | return GetData(true); 152 | } 153 | 154 | public IList GetData(bool rethrowFirst) where T : class 155 | { 156 | if (rethrowFirst) 157 | RethrowFirstException(); 158 | 159 | if (_data == null) 160 | return []; 161 | else 162 | return Array.ConvertAll(_data, p => (T?)p!); 163 | } 164 | 165 | public IList GetErrors() 166 | { 167 | return _errors; 168 | } 169 | 170 | public bool HasErrors() 171 | { 172 | return _errors.Length > 0; 173 | } 174 | 175 | public void RethrowFirstException() 176 | { 177 | if (_errors.Length > 0) 178 | throw new AggregateException(_errors); 179 | } 180 | } 181 | 182 | } 183 | -------------------------------------------------------------------------------- /CodexMicroORM.CodexV1CompatLayer/CodexMicroORM.CodexV1CompatLayer/Stubs.cs: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Copyright 2024 CodeX Enterprises LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | Major Changes: 17 | 06/2018 0.7 Initial release (Joel Champagne) 18 | ***********************************************************************/ 19 | #nullable enable 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Text; 23 | 24 | namespace CodeXFramework.BaseEntity 25 | { 26 | /// 27 | /// This class allows use of new EntityDataScope in legacy code but it performs NO ACTIONS. This is all covered now by service and connection scopes, it's encouraged that you change EntityDataScope references - but this at least lets code break less. 28 | /// 29 | public class EntityDataScope : IDisposable 30 | { 31 | 32 | #region IDisposable Support 33 | private bool disposedValue = false; // To detect redundant calls 34 | 35 | protected virtual void Dispose(bool disposing) 36 | { 37 | if (!disposedValue) 38 | { 39 | disposedValue = true; 40 | } 41 | } 42 | 43 | // This code added to correctly implement the disposable pattern. 44 | public void Dispose() 45 | { 46 | Dispose(true); 47 | } 48 | #endregion 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /CodexMicroORM.Core/Base/AttributeInitializer.cs: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Copyright 2024 CodeX Enterprises LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | Major Changes: 17 | 06/2018 0.7 Initial release (Joel Champagne) 18 | ***********************************************************************/ 19 | using CodexMicroORM.Core.Services; 20 | using System; 21 | using System.Linq; 22 | using System.Reflection; 23 | using System.Threading.Tasks; 24 | #nullable enable 25 | 26 | namespace CodexMicroORM.Core 27 | { 28 | public static class AttributeInitializer 29 | { 30 | public static bool SilentlyContinue 31 | { 32 | get; 33 | set; 34 | } = false; 35 | 36 | public static Action<(Type type, string? prop, Type attr)>? PreviewHandler 37 | { 38 | get; 39 | set; 40 | } = null; 41 | 42 | public static void Apply(params Assembly[] args) 43 | { 44 | if (args?.Length == 0) 45 | { 46 | args = AppDomain.CurrentDomain.GetAssemblies(); 47 | } 48 | 49 | // Traverse provided assemblies, looking for classes that implement attributes of interest 50 | Parallel.ForEach(args ?? [], (a) => 51 | { 52 | Parallel.ForEach(a.GetTypes(), (t) => 53 | { 54 | try 55 | { 56 | var pkAttr = t.GetCustomAttribute(); 57 | 58 | if (pkAttr != null) 59 | { 60 | PreviewHandler?.Invoke((t, null, typeof(EntityPrimaryKeyAttribute))); 61 | if (pkAttr.ShadowType != null) 62 | { 63 | typeof(KeyService).GetMethod("RegisterKeyWithType")!.MakeGenericMethod(t).Invoke(null, [ pkAttr.Fields.First(), pkAttr.ShadowType ]); 64 | } 65 | else 66 | { 67 | typeof(KeyService).GetMethod("RegisterKey")!.MakeGenericMethod(t).Invoke(null, [ pkAttr.Fields ]); 68 | } 69 | 70 | foreach (var prop in t.GetProperties()) 71 | { 72 | var maxLenAttr = prop.GetCustomAttribute(); 73 | 74 | if (maxLenAttr != null) 75 | { 76 | PreviewHandler?.Invoke((t, prop.Name, typeof(EntityMaxLengthAttribute))); 77 | typeof(ValidationService).GetMethod("RegisterMaxLength")!.MakeGenericMethod(t).Invoke(null, [ prop.Name, maxLenAttr.Length ]); 78 | } 79 | 80 | var defValAttr = prop.GetCustomAttribute(); 81 | 82 | if (defValAttr != null) 83 | { 84 | PreviewHandler?.Invoke((t, prop.Name, typeof(EntityDefaultValueAttribute))); 85 | typeof(DBService).GetMethod("RegisterDefault")!.MakeGenericMethod(t, prop.PropertyType).Invoke(null, [ prop.Name, defValAttr.Value.CoerceType(prop.PropertyType) ]); 86 | } 87 | 88 | var reqValAttr = prop.GetCustomAttribute(); 89 | 90 | if (reqValAttr != null) 91 | { 92 | PreviewHandler?.Invoke((t, prop.Name, typeof(EntityRequiredAttribute))); 93 | typeof(ValidationService).GetMethod("RegisterRequired", [ typeof(string) ])!.MakeGenericMethod(t, prop.PropertyType).Invoke(null, [ prop.Name ]); 94 | } 95 | 96 | var ignBindAttr = prop.GetCustomAttribute(); 97 | 98 | if (ignBindAttr != null) 99 | { 100 | PreviewHandler?.Invoke((t, prop.Name, typeof(EntityIgnoreBindingAttribute))); 101 | } 102 | 103 | var treatROAttr = prop.GetCustomAttribute(); 104 | 105 | if (treatROAttr != null) 106 | { 107 | CEF.RegisterPropertyNameTreatReadOnly(prop.Name); 108 | } 109 | } 110 | } 111 | 112 | var dnsAttr = t.GetCustomAttribute(); 113 | 114 | if (dnsAttr != null) 115 | { 116 | PreviewHandler?.Invoke((t, null, typeof(EntityDoNotSaveAttribute))); 117 | typeof(ServiceScope).GetMethod("RegisterDoNotSave")!.MakeGenericMethod(t).Invoke(null, []); 118 | } 119 | 120 | foreach (EntityAdditionalPropertiesAttribute addPropAttr in t.GetCustomAttributes()) 121 | { 122 | PreviewHandler?.Invoke((t, null, typeof(EntityAdditionalPropertiesAttribute))); 123 | typeof(ServiceScope).GetMethod("AddAdditionalPropertyHost")!.MakeGenericMethod(t).Invoke(null, [ addPropAttr.PropertyName ]); 124 | } 125 | } 126 | catch 127 | { 128 | if (!SilentlyContinue) 129 | { 130 | throw; 131 | } 132 | } 133 | 134 | try 135 | { 136 | foreach (var prop in t.GetProperties()) 137 | { 138 | var dateStoreAttr = prop.GetCustomAttribute(); 139 | 140 | if (dateStoreAttr != null && dateStoreAttr.StorageMode != PropertyDateStorage.None) 141 | { 142 | PreviewHandler?.Invoke((t, prop.Name, typeof(EntityDateHandlingAttribute))); 143 | typeof(ServiceScope).GetMethod("SetDateStorageMode")!.MakeGenericMethod(t).Invoke(null, [ prop.Name, dateStoreAttr.StorageMode ]); 144 | } 145 | } 146 | } 147 | catch 148 | { 149 | if (!SilentlyContinue) 150 | { 151 | throw; 152 | } 153 | } 154 | 155 | try 156 | { 157 | var cacheAttr = t.GetCustomAttribute(); 158 | 159 | if (cacheAttr != null) 160 | { 161 | PreviewHandler?.Invoke((t, null, typeof(EntityCacheRecommendAttribute))); 162 | 163 | typeof(ServiceScope).GetMethod("SetCacheBehavior")!.MakeGenericMethod(t).Invoke(null, [ CacheBehavior.MaximumDefault ]); 164 | 165 | if (cacheAttr.OnlyMemory.HasValue) 166 | { 167 | typeof(ServiceScope).GetMethod("SetCacheOnlyMemory")!.MakeGenericMethod(t).Invoke(null, [ cacheAttr.OnlyMemory.Value ]); 168 | } 169 | 170 | if (cacheAttr.IntervalMinutes.HasValue) 171 | { 172 | typeof(ServiceScope).GetMethod("SetCacheSeconds")!.MakeGenericMethod(t).Invoke(null, [ cacheAttr.IntervalMinutes.Value * 60 ]); 173 | } 174 | } 175 | } 176 | catch 177 | { 178 | if (!SilentlyContinue) 179 | { 180 | throw; 181 | } 182 | } 183 | 184 | try 185 | { 186 | var schemaAttr = t.GetCustomAttribute(); 187 | 188 | if (schemaAttr != null) 189 | { 190 | PreviewHandler?.Invoke((t, null, typeof(EntitySchemaNameAttribute))); 191 | typeof(DBService).GetMethod("RegisterSchema")!.MakeGenericMethod(t).Invoke(null, [ schemaAttr.Name ]); 192 | } 193 | } 194 | catch 195 | { 196 | if (!SilentlyContinue) 197 | { 198 | throw; 199 | } 200 | } 201 | 202 | try 203 | { 204 | var relAttr = t.GetCustomAttribute(); 205 | 206 | if (relAttr != null) 207 | { 208 | PreviewHandler?.Invoke((t, null, typeof(EntityRelationshipsAttribute))); 209 | typeof(KeyService).GetMethod("RegisterRelationship")!.MakeGenericMethod(t).Invoke(null, [ relAttr.Relations ]); 210 | } 211 | } 212 | catch 213 | { 214 | if (!SilentlyContinue) 215 | { 216 | throw; 217 | } 218 | } 219 | }); 220 | }); 221 | } 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /CodexMicroORM.Core/Base/Attributes.cs: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Copyright 2024 CodeX Enterprises LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | Major Changes: 17 | 06/2018 0.7 Initial release (Joel Champagne) 18 | ***********************************************************************/ 19 | using CodexMicroORM.Core.Services; 20 | using System; 21 | using System.Collections.Concurrent; 22 | using System.Collections.Generic; 23 | using System.Text; 24 | #nullable enable 25 | 26 | namespace CodexMicroORM.Core 27 | { 28 | [AttributeUsage(AttributeTargets.Class)] 29 | public sealed class EntityCacheRecommendAttribute : Attribute 30 | { 31 | public EntityCacheRecommendAttribute(int intervalMinutes, bool onlyMemory) 32 | { 33 | IntervalMinutes = intervalMinutes; 34 | OnlyMemory = onlyMemory; 35 | } 36 | 37 | public EntityCacheRecommendAttribute(int intervalMinutes) 38 | { 39 | IntervalMinutes = intervalMinutes; 40 | } 41 | 42 | public int? IntervalMinutes 43 | { 44 | get; 45 | private set; 46 | } 47 | 48 | public bool? OnlyMemory 49 | { 50 | get; 51 | private set; 52 | } = true; 53 | } 54 | 55 | public sealed class EntityIgnoreBindingAttribute : Attribute 56 | { 57 | } 58 | 59 | public sealed class EntityDoNotSaveAttribute : Attribute 60 | { 61 | } 62 | 63 | [AttributeUsage(AttributeTargets.Class)] 64 | public sealed class EntityPrimaryKeyAttribute : Attribute 65 | { 66 | public string[] Fields 67 | { 68 | get; 69 | private set; 70 | } 71 | 72 | public Type? ShadowType 73 | { 74 | get; 75 | private set; 76 | } 77 | 78 | public EntityPrimaryKeyAttribute(params string[] fields) 79 | { 80 | Fields = fields; 81 | } 82 | 83 | /// 84 | /// This overload supports initialization of an entity with a field that might not exist as a property and instead should be implemented using a shadow property of a specific desired type. 85 | /// 86 | /// 87 | /// 88 | public EntityPrimaryKeyAttribute(string field, Type datatype) 89 | { 90 | ShadowType = datatype; 91 | Fields = [ field ]; 92 | } 93 | } 94 | 95 | [AttributeUsage(AttributeTargets.Class)] 96 | public sealed class EntitySchemaNameAttribute : Attribute 97 | { 98 | public string Name 99 | { 100 | get; 101 | private set; 102 | } 103 | 104 | public EntitySchemaNameAttribute(string name) 105 | { 106 | Name = name; 107 | } 108 | } 109 | 110 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] 111 | public sealed class EntityAdditionalPropertiesAttribute : Attribute 112 | { 113 | public EntityAdditionalPropertiesAttribute(string propname) 114 | { 115 | PropertyName = propname; 116 | } 117 | 118 | public string PropertyName 119 | { 120 | get; 121 | private set; 122 | } 123 | } 124 | 125 | public sealed class EntityDateHandlingAttribute : Attribute 126 | { 127 | public EntityDateHandlingAttribute(PropertyDateStorage mode) 128 | { 129 | StorageMode = mode; 130 | } 131 | 132 | public PropertyDateStorage StorageMode 133 | { 134 | get; 135 | private set; 136 | } 137 | } 138 | 139 | [AttributeUsage(AttributeTargets.Property)] 140 | public sealed class PropertyTreatAsIfReadOnlyAttribute : Attribute 141 | { 142 | public PropertyTreatAsIfReadOnlyAttribute() 143 | { 144 | } 145 | } 146 | 147 | [AttributeUsage(AttributeTargets.Property)] 148 | public sealed class EntityRequiredAttribute : Attribute 149 | { 150 | public EntityRequiredAttribute() 151 | { 152 | } 153 | } 154 | 155 | [AttributeUsage(AttributeTargets.Property)] 156 | public sealed class EntityMaxLengthAttribute : Attribute 157 | { 158 | public int Length 159 | { 160 | get; 161 | private set; 162 | } 163 | 164 | public EntityMaxLengthAttribute(int length) 165 | { 166 | if (length < 0) 167 | length = int.MaxValue; 168 | 169 | Length = length; 170 | } 171 | } 172 | 173 | [AttributeUsage(AttributeTargets.Property)] 174 | public sealed class EntityDefaultValueAttribute : Attribute 175 | { 176 | public string Value 177 | { 178 | get; 179 | private set; 180 | } 181 | 182 | public EntityDefaultValueAttribute(string value) 183 | { 184 | Value = value; 185 | } 186 | } 187 | 188 | [AttributeUsage(AttributeTargets.Class)] 189 | public sealed class EntityRelationshipsAttribute : Attribute 190 | { 191 | public TypeChildRelationship[] Relations 192 | { 193 | get; 194 | private set; 195 | } 196 | 197 | private readonly static ConcurrentDictionary _typeCache = []; 198 | 199 | private Type? FindTypeByName(string name) 200 | { 201 | if (_typeCache.TryGetValue(name, out var t)) 202 | { 203 | return t; 204 | } 205 | 206 | foreach (var a in AppDomain.CurrentDomain.GetAssemblies()) 207 | { 208 | t = a.GetType(name, false); 209 | 210 | if (t != null) 211 | { 212 | _typeCache[name] = t; 213 | return t; 214 | } 215 | } 216 | 217 | return null; 218 | } 219 | 220 | public EntityRelationshipsAttribute(params string[] relations) 221 | { 222 | List list = []; 223 | 224 | foreach (var rel in relations) 225 | { 226 | var typeAndFields = rel.Split('\\'); 227 | 228 | if (typeAndFields.Length != 4) 229 | { 230 | throw new CEFInvalidStateException(InvalidStateType.BadParameterValue, $"Invalid relationship spec '{rel}'."); 231 | } 232 | 233 | var childType = FindTypeByName(typeAndFields[0]) ?? throw new CEFInvalidStateException(InvalidStateType.BadParameterValue, $"Could not find child type '{typeAndFields[0]}'."); ; 234 | 235 | var fields = typeAndFields[1].Split(','); 236 | 237 | if (fields.Length < 1) 238 | { 239 | throw new CEFInvalidStateException(InvalidStateType.BadParameterValue, $"Invalid relationship spec '{rel}'."); 240 | } 241 | 242 | var tcr = (TypeChildRelationship) typeof(TypeChildRelationship).GetMethod("Create")!.MakeGenericMethod(childType).Invoke(null, [ fields ])!; 243 | 244 | if (!string.IsNullOrEmpty(typeAndFields[2])) 245 | { 246 | tcr = tcr.MapsToChildProperty(typeAndFields[2]); 247 | } 248 | 249 | if (!string.IsNullOrEmpty(typeAndFields[3])) 250 | { 251 | tcr = tcr.MapsToParentProperty(typeAndFields[3]); 252 | } 253 | 254 | list.Add(tcr); 255 | } 256 | 257 | Relations = list.ToArray(); 258 | } 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /CodexMicroORM.Core/Base/BaseModel.cs: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Copyright 2024 CodeX Enterprises LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | Major Changes: 17 | ***********************************************************************/ 18 | using System; 19 | using System.Collections.Generic; 20 | using System.ComponentModel; 21 | using System.Runtime.CompilerServices; 22 | 23 | namespace CodexMicroORM.Core 24 | { 25 | public class BaseModel : INotifyPropertyChanged 26 | { 27 | private readonly Dictionary> _dependentProps = []; 28 | 29 | protected void RegisterLinkedProperty(string sourceprop, string firesprop) 30 | { 31 | _dependentProps.TryGetValue(sourceprop, out List l); 32 | l ??= []; 33 | 34 | l.Add(firesprop); 35 | 36 | _dependentProps[sourceprop] = l; 37 | } 38 | 39 | private bool _isBusy = false; 40 | public bool IsBusy 41 | { 42 | get { return _isBusy; } 43 | set { SetProperty(ref _isBusy, value); } 44 | } 45 | 46 | private bool _isLoading = false; 47 | public bool IsLoading 48 | { 49 | get { return _isLoading; } 50 | set { SetProperty(ref _isLoading, value); } 51 | } 52 | 53 | protected bool SetProperty(ref T backingStore, T value, 54 | [CallerMemberName] string propertyName = "", 55 | Action onChanged = null) 56 | { 57 | if (EqualityComparer.Default.Equals(backingStore, value)) 58 | { 59 | return false; 60 | } 61 | 62 | backingStore = value; 63 | onChanged?.Invoke(); 64 | OnPropertyChanged(propertyName); 65 | 66 | if (_dependentProps.TryGetValue(propertyName, out var l)) 67 | { 68 | foreach (var dp in l) 69 | { 70 | OnPropertyChanged(dp); 71 | } 72 | } 73 | 74 | return true; 75 | } 76 | 77 | #region INotifyPropertyChanged 78 | public event PropertyChangedEventHandler PropertyChanged; 79 | protected void OnPropertyChanged([CallerMemberName] string propertyName = "") 80 | { 81 | var changed = PropertyChanged; 82 | if (changed == null) 83 | { 84 | return; 85 | } 86 | 87 | changed.Invoke(this, new PropertyChangedEventArgs(propertyName)); 88 | } 89 | #endregion 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /CodexMicroORM.Core/Base/Enums.cs: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Copyright 2024 CodeX Enterprises LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | Major Changes: 17 | 12/2017 0.2 Initial release (Joel Champagne) 18 | ***********************************************************************/ 19 | using System; 20 | 21 | namespace CodexMicroORM.Core 22 | { 23 | [Flags] 24 | public enum DBSaveTriggerFlags 25 | { 26 | Insert = 1, 27 | Update = 2, 28 | Delete = 4, 29 | Before = 32, 30 | After = 64 31 | } 32 | 33 | public enum PropertyDateStorage 34 | { 35 | None = 0, 36 | TwoWayConvertUtc = 1, 37 | TwoWayConvertUtcOnlyWithTime = 2 38 | } 39 | 40 | public enum SerializationType 41 | { 42 | Array = 0, 43 | ObjectWithSchemaType1AndRows = 1 44 | } 45 | 46 | [Flags] 47 | public enum SerializationMode 48 | { 49 | ObjectState = 1, 50 | OnlyChanged = 2, 51 | IncludeReadOnlyProps = 4, 52 | IncludeNull = 8, 53 | OriginalForConcurrency = 16, 54 | OnlyCLRProperties = 32, 55 | IncludeType = 64, 56 | SingleLevel = 128, 57 | ExtendedInfoAsShadowProps = 256, 58 | Default = 9, 59 | OverWire = 25, 60 | OverWireOnlyChanges = 27 61 | } 62 | 63 | [Flags] 64 | public enum CacheBehavior 65 | { 66 | Off = 0, 67 | IdentityBased = 1, 68 | QueryBased = 2, 69 | ConvertQueryToIdentity = 4, 70 | OnlyForAllQuery = 8, 71 | ForAllDoesntConvertToIdentity = 16, 72 | Default = 5, 73 | ListCentricDefault = 29, 74 | MaximumDefault = 23 75 | } 76 | 77 | public enum ObjectState 78 | { 79 | Unchanged = 0, 80 | Modified = 1, 81 | Added = 2, 82 | Deleted = 3, 83 | Unlinked = 4, 84 | ModifiedPriority = 5 85 | } 86 | 87 | [Flags] 88 | public enum BulkRules 89 | { 90 | Never = 0, 91 | Always = 1, 92 | Threshold = 2, 93 | LeafOnly = 4, 94 | ByType = 8, 95 | Default = 6 96 | } 97 | 98 | public enum WrappingAction 99 | { 100 | NoneOrProvisionedAlready = 0, 101 | PreCodeGen = 1, 102 | Dynamic = 2, 103 | DataTable = 3 104 | } 105 | 106 | public enum ScopeMode 107 | { 108 | UseAmbient = 0, 109 | CreateNew = 1 110 | } 111 | 112 | public enum FrameworkWarningType 113 | { 114 | RetrievalIdentity = 1 115 | } 116 | 117 | /// 118 | /// Added: 1.2 119 | /// When retrieving possibly multiple records into an Entity Set, if the underlying type has a key defined and if the incoming data has duplicate keys, this setting determines how to handle the situation. 120 | /// None is is close to the existing behavior, pre 1.2 - zero overhead added to retrieval process, behaves like MaintainIdentityAndWarn with no warnings issued. 121 | /// MaintainIdentityAndWarn is close to the existing behavior, pre 1.2. In this case, the first instance is kept/returned, a warning is issued for each attempt to return a new row, values are copied to the first instance, such that there will only be as many instances tracked as there are unique values. 122 | /// WireFirst will keep a list of all instances that were attempted to be returned but only the first per key value will be "linkable" within the object graph. 123 | /// WithShadowProp will effectively act as a key-override such that the type or instance will be treated similar to a non-keyed type, but with a shadow property to hold a system-generated key value (_ID). This makes it possible to have multiple instances of the same key value, but they will not be linkable within the object graph. 124 | /// 125 | public enum RetrievalIdentityMode 126 | { 127 | MaintainIdentityAndWarn = 0, 128 | ThrowErrorOnDuplicate = 1, 129 | AllowMultipleWireFirst = 2, 130 | AllowMultipleWithShadowProp = 3 131 | } 132 | 133 | [Flags] 134 | public enum RetrievalPostProcessing 135 | { 136 | None = 0, 137 | PropertyGroups = 1, 138 | ParentInstancesWithoutCLRProperties = 2, 139 | PropertyNameFixups = 4, 140 | Default = 7 141 | } 142 | 143 | /// 144 | /// Identifies the type of validation failure presented by the validation engine. Can also be used as a filter to represent what types of validations are of interest. As such, is a flag that can be combined as a filter, or discrete values to indicate specific failures. 145 | /// Why are some types such as required field validation not included in default save validation? The database is also effectively doing this too, and we assume an app might have their own method of validation, so picking the least-overhead approach as the default: but easy to override at a Global level. 146 | /// 147 | [Flags] 148 | public enum ValidationErrorCode 149 | { 150 | None = 0, 151 | MissingRequired = 65536, 152 | TooLarge = (65536 * 2), 153 | CustomError = (65536 * 4), 154 | NumericRange = (65536 * 8), 155 | IllegalUpdate = (65536 * 16), 156 | SaveFailDefault = MissingRequired | TooLarge | CustomError | NumericRange | IllegalUpdate 157 | } 158 | 159 | [Flags] 160 | public enum RelationTypes 161 | { 162 | None = 0, 163 | Parents = 1, 164 | Children = 2, 165 | Both = 3 166 | } 167 | 168 | [Flags] 169 | public enum WrappingSupport 170 | { 171 | None = 0, 172 | Notifications = 1, 173 | OriginalValues = 2, 174 | PropertyBag = 4, 175 | DataErrors = 8, 176 | All = 15 177 | } 178 | 179 | [Flags] 180 | public enum MergeBehavior 181 | { 182 | SilentMerge = 1, 183 | FailIfDifferent = 2, 184 | CheckScopeIfPending = 4, 185 | Default = 5 186 | } 187 | 188 | public enum DeleteCascadeAction 189 | { 190 | None = 0, 191 | Cascade = 1, 192 | SetNull = 2, 193 | Fail = 3 194 | } 195 | 196 | public enum DateConversionMode 197 | { 198 | None = 0, 199 | ToGMTAlways = 1, 200 | ToGMTWhenHasTime = 2 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /CodexMicroORM.Core/Base/Exceptions.cs: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Copyright 2024 CodeX Enterprises LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | Major Changes: 17 | 12/2017 0.2 Initial release (Joel Champagne) 18 | 02/2018 0.2.4 CEFValidationException (Joel Champagne) 19 | ***********************************************************************/ 20 | #nullable enable 21 | using System; 22 | using System.Collections.Generic; 23 | using System.Runtime.CompilerServices; 24 | using System.Text; 25 | 26 | namespace CodexMicroORM.Core 27 | { 28 | public enum InvalidStateType 29 | { 30 | Undefined = 0, 31 | ArgumentNull = 1, 32 | LowLevelState = 2, 33 | ObjectTrackingIssue = 3, 34 | MissingService = 4, 35 | MissingKey = 5, 36 | BadParameterValue = 6, 37 | Serialization = 7, 38 | BadAction = 8, 39 | SQLLayer = 9, 40 | DataTypeIssue = 10, 41 | MissingInit = 11, 42 | MissingServiceState = 12 43 | } 44 | 45 | public class CEFInvalidStateException : InvalidOperationException 46 | { 47 | private readonly string? _message; 48 | private readonly string? _method; 49 | 50 | public CEFInvalidStateException(InvalidStateType failtype, [CallerMemberName] string? caller = null) : base() 51 | { 52 | FailType = failtype; 53 | _method = caller; 54 | } 55 | 56 | public CEFInvalidStateException(InvalidStateType failtype, string message, [CallerMemberName] string? caller = null) : base() 57 | { 58 | FailType = failtype; 59 | _message = message; 60 | _method = caller; 61 | } 62 | 63 | public InvalidStateType FailType { get; } = InvalidStateType.Undefined; 64 | 65 | public override string Message 66 | { 67 | get 68 | { 69 | string s = FailType switch 70 | { 71 | InvalidStateType.ArgumentNull => "Argument value missing.", 72 | InvalidStateType.LowLevelState => "Invalid or corrupted state (low-level).", 73 | InvalidStateType.ObjectTrackingIssue => "Object tracking data is missing or corrupt.", 74 | InvalidStateType.MissingService => "Missing service.", 75 | InvalidStateType.MissingKey => "Missing key definition.", 76 | InvalidStateType.BadParameterValue => "Bad parameter value.", 77 | InvalidStateType.Serialization => "Serialization error.", 78 | InvalidStateType.BadAction => "Bad attempted action.", 79 | InvalidStateType.SQLLayer => "SQL-related error.", 80 | InvalidStateType.DataTypeIssue => "Data type / conversion issue.", 81 | InvalidStateType.MissingInit => "Missing initialization (framework settings).", 82 | InvalidStateType.MissingServiceState => "Missing service state data.", 83 | _ => "Invalid operation.", 84 | }; 85 | 86 | StringBuilder sb = new(); 87 | sb.Append(s); 88 | sb.Append(" This indicates a possible framework or framework usage issue."); 89 | 90 | if (!string.IsNullOrEmpty(_message)) 91 | { 92 | sb.Append(" "); 93 | 94 | switch (FailType) 95 | { 96 | case InvalidStateType.ArgumentNull: 97 | sb.Append($"Argument: {_message}."); 98 | break; 99 | 100 | case InvalidStateType.MissingKey: 101 | sb.Append($"Type: {_message}."); 102 | break; 103 | 104 | case InvalidStateType.MissingServiceState: 105 | sb.Append($"Service: {_message}"); 106 | break; 107 | 108 | default: 109 | sb.Append(_message); 110 | break; 111 | } 112 | } 113 | 114 | if (!string.IsNullOrEmpty(_method)) 115 | { 116 | sb.Append(" ("); 117 | sb.Append("In: "); 118 | sb.Append(_method); 119 | sb.Append(".)"); 120 | } 121 | 122 | return sb.ToString(); 123 | } 124 | } 125 | 126 | public CEFInvalidStateException() 127 | { 128 | } 129 | 130 | public CEFInvalidStateException(string message, [CallerMemberName] string? caller = null) : base(message) 131 | { 132 | _message = message; 133 | _method = caller; 134 | } 135 | 136 | public CEFInvalidStateException(string message, Exception innerException) : base(message, innerException) 137 | { 138 | _message = message; 139 | } 140 | 141 | public CEFInvalidStateException(InvalidStateType failtype, string message, Exception innerException, [CallerMemberName] string? caller = null) : base(message, innerException) 142 | { 143 | FailType = failtype; 144 | _message = message; 145 | _method = caller; 146 | } 147 | 148 | public CEFInvalidStateException(string message) : base(message) 149 | { 150 | } 151 | } 152 | 153 | public class CEFConstraintException : Exception 154 | { 155 | public CEFConstraintException(string msg) : base(msg) 156 | { 157 | } 158 | 159 | public CEFConstraintException(string msg, Exception inner) : base(msg, inner) 160 | { 161 | } 162 | 163 | public CEFConstraintException() 164 | { 165 | } 166 | } 167 | 168 | public class CEFTimeoutException : TimeoutException 169 | { 170 | public CEFTimeoutException(string msg) : base(msg) 171 | { 172 | } 173 | 174 | public CEFTimeoutException(string msg, Exception inner) : base(msg, inner) 175 | { 176 | } 177 | 178 | public CEFTimeoutException() 179 | { 180 | } 181 | } 182 | 183 | public class CEFInvalidDataException : InvalidOperationException 184 | { 185 | public CEFInvalidDataException(string msg) : base(msg) 186 | { 187 | } 188 | } 189 | 190 | public class CEFValidationException : ApplicationException 191 | { 192 | private readonly IEnumerable<(ValidationErrorCode error, string message)>? _messages = null; 193 | 194 | public CEFValidationException(string msg) : base(msg) 195 | { 196 | } 197 | 198 | public CEFValidationException(string msg, Exception inner) : base(msg, inner) 199 | { 200 | } 201 | 202 | public CEFValidationException(string msg, IEnumerable<(ValidationErrorCode error, string message)> messages) : base(msg) 203 | { 204 | _messages = messages; 205 | } 206 | 207 | public IEnumerable<(ValidationErrorCode error, string message)>? Messages => _messages; 208 | 209 | public CEFValidationException() 210 | { 211 | } 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /CodexMicroORM.Core/Base/ServiceScope.TrackedObject.cs: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Copyright 2024 CodeX Enterprises LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | Major Changes: 17 | 05/2018 0.6 Split from servicescope.cs (Joel Champagne) 18 | ***********************************************************************/ 19 | #nullable enable 20 | using System; 21 | using System.Collections.Generic; 22 | using CodexMicroORM.Core.Services; 23 | using CodexMicroORM.Core.Collections; 24 | using System.ComponentModel; 25 | 26 | namespace CodexMicroORM.Core 27 | { 28 | public sealed partial class ServiceScope 29 | { 30 | public sealed class TrackedObject : ICEFIndexedListItem 31 | { 32 | public string? BaseName { get; set; } 33 | public Type? BaseType { get; set; } 34 | public CEFWeakReference? Target { get; set; } 35 | public CEFWeakReference? Wrapper { get; set; } 36 | public ICEFInfraWrapper? Infra { get; set; } 37 | public List? Services { get; set; } 38 | 39 | public bool ValidTarget 40 | { 41 | get 42 | { 43 | return (Target?.IsAlive).GetValueOrDefault(); 44 | } 45 | } 46 | 47 | public object? GetTarget() 48 | { 49 | if ((Target?.IsAlive).GetValueOrDefault() && Target?.Target != null) 50 | return Target.Target; 51 | 52 | return null; 53 | } 54 | 55 | public ICEFInfraWrapper? GetInfra() 56 | { 57 | if ((Target?.IsAlive).GetValueOrDefault()) 58 | { 59 | if (Infra?.GetWrappedObject() != null) 60 | { 61 | return Infra; 62 | } 63 | } 64 | 65 | return null; 66 | } 67 | 68 | public INotifyPropertyChanged? GetNotifyFriendly() 69 | { 70 | if (GetTarget() is INotifyPropertyChanged test1) 71 | return test1; 72 | 73 | if (GetWrapper() is INotifyPropertyChanged test2) 74 | return test2; 75 | 76 | return GetInfra() as INotifyPropertyChanged; 77 | } 78 | 79 | public ICEFInfraWrapper? GetCreateInfra(ServiceScope? ss = null) 80 | { 81 | var infra = GetInfra(); 82 | 83 | if (infra != null) 84 | return infra; 85 | 86 | // Must succeed so create an infra wrapper! 87 | var wt = GetWrapperTarget(); 88 | 89 | if (wt == null) 90 | throw new CEFInvalidStateException(InvalidStateType.ObjectTrackingIssue); 91 | 92 | Infra = WrappingHelper.CreateInfraWrapper(WrappingSupport.All, WrappingAction.Dynamic, false, wt, null, null, null, ss ?? CEF.CurrentServiceScope); 93 | return Infra; 94 | } 95 | 96 | public ICEFWrapper? GetWrapper() 97 | { 98 | if ((Wrapper?.IsAlive).GetValueOrDefault() && Wrapper?.Target != null) 99 | return Wrapper.Target as ICEFWrapper; 100 | 101 | return null; 102 | } 103 | 104 | public object GetInfraWrapperTarget() 105 | { 106 | return GetInfra() ?? GetWrapper() ?? GetTarget() ?? throw new CEFInvalidStateException(InvalidStateType.ObjectTrackingIssue); 107 | } 108 | 109 | public object? GetWrapperTarget() 110 | { 111 | return GetWrapper() ?? GetTarget(); 112 | } 113 | 114 | public object? GetValue(string propName, bool unwrap) 115 | { 116 | switch (propName) 117 | { 118 | case nameof(BaseName): 119 | return BaseName; 120 | 121 | case nameof(BaseType): 122 | return BaseType; 123 | 124 | case nameof(Target): 125 | if (unwrap) 126 | return Target != null && Target.IsAlive ? Target.Target : null; 127 | else 128 | return Target; 129 | 130 | case nameof(Wrapper): 131 | if (unwrap) 132 | return Wrapper != null && Wrapper.IsAlive ? Wrapper.Target : null; 133 | else 134 | return Wrapper; 135 | 136 | case nameof(Infra): 137 | return Infra; 138 | 139 | case nameof(Services): 140 | return Services; 141 | } 142 | throw new NotSupportedException("Unsupported property name."); 143 | } 144 | 145 | public bool IsAlive 146 | { 147 | get 148 | { 149 | return !((!(Target?.IsAlive).GetValueOrDefault() || Target?.Target == null) 150 | && (!(Wrapper?.IsAlive).GetValueOrDefault() || Wrapper?.Target == null)); 151 | } 152 | } 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /CodexMicroORM.Core/Base/Settings.cs: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Copyright 2024 CodeX Enterprises LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | Major Changes: 17 | 12/2017 0.2 Initial release (Joel Champagne) 18 | ***********************************************************************/ 19 | #nullable enable 20 | using System; 21 | using System.Collections.Concurrent; 22 | using System.Collections.Generic; 23 | using System.Data; 24 | using CodexMicroORM.Core.Services; 25 | 26 | namespace CodexMicroORM.Core 27 | { 28 | /// 29 | /// Settings applicable to service scopes. 30 | /// 31 | [Serializable] 32 | public sealed class ServiceScopeSettings 33 | { 34 | public bool EntitySetUsesUnwrapped 35 | { 36 | get; 37 | set; 38 | } = false; 39 | 40 | public bool InitializeNullCollections 41 | { 42 | get; 43 | set; 44 | } = Globals.DefaultInitializeNullCollections; 45 | 46 | public SerializationMode SerializationMode 47 | { 48 | get; 49 | set; 50 | } = Globals.DefaultSerializationMode; 51 | 52 | public bool? UseAsyncSave 53 | { 54 | get; 55 | set; 56 | } = null; 57 | 58 | public bool? AsyncCacheUpdates 59 | { 60 | get; 61 | set; 62 | } = null; 63 | 64 | public CacheBehavior? CacheBehavior 65 | { 66 | get; 67 | set; 68 | } = null; 69 | 70 | public bool DisableCaching 71 | { 72 | get; 73 | set; 74 | } = false; 75 | 76 | public int? GlobalCacheDuration 77 | { 78 | get; 79 | set; 80 | } = null; 81 | 82 | public RetrievalPostProcessing RetrievalPostProcessing 83 | { 84 | get; 85 | set; 86 | } = Globals.DefaultRetrievalPostProcessing; 87 | 88 | public MergeBehavior MergeBehavior 89 | { 90 | get; 91 | set; 92 | } = Globals.DefaultMergeBehavior; 93 | 94 | private Func _getLastUpdatedBy = () => { return Globals.GetCurrentUser(); }; 95 | private bool _getLastUpdatedBySet = false; 96 | 97 | public bool GetLastUpdatedByChanged => _getLastUpdatedBySet; 98 | 99 | public Func GetLastUpdatedBy 100 | { 101 | get 102 | { 103 | return _getLastUpdatedBy; 104 | } 105 | set 106 | { 107 | _getLastUpdatedBy = value; 108 | _getLastUpdatedBySet = true; 109 | } 110 | } 111 | 112 | public int EstimatedScopeSize 113 | { 114 | get; 115 | set; 116 | } = Environment.ProcessorCount * 7; 117 | 118 | public bool? ConnectionScopePerThread 119 | { 120 | get; 121 | set; 122 | } = null; 123 | 124 | //[ThreadStatic] <- doesn't make sense as static? 125 | public bool CanDispose = true; 126 | } 127 | 128 | public sealed class JsonSerializationSettings 129 | { 130 | public SerializationType SerializationType 131 | { 132 | get; 133 | set; 134 | } = SerializationType.Array; 135 | 136 | public string SchemaName 137 | { 138 | get; 139 | set; 140 | } = "Schema"; 141 | 142 | public string SchemaFieldNameName 143 | { 144 | get; 145 | set; 146 | } = "Name"; 147 | 148 | public string SchemaFieldTypeName 149 | { 150 | get; 151 | set; 152 | } = "Type"; 153 | 154 | public string SchemaFieldRequiredName 155 | { 156 | get; 157 | set; 158 | } = "Required"; 159 | 160 | public string? DataRootName 161 | { 162 | get; 163 | set; 164 | } 165 | 166 | public Func GetDataType 167 | { 168 | get; 169 | set; 170 | } = (tn) => 171 | { 172 | return Type.GetType($"System.{tn}") ?? throw new CEFInvalidStateException(InvalidStateType.DataTypeIssue, $"Failed to get type for System.{tn}."); ; 173 | }; 174 | } 175 | 176 | /// 177 | /// Settings applicable to connection scopes. 178 | /// 179 | [Serializable] 180 | public sealed class ConnectionScopeSettings 181 | { 182 | public ScopeMode? ScopeMode 183 | { 184 | get; 185 | set; 186 | } = null; 187 | 188 | public string? ConnectionStringOverride 189 | { 190 | get; 191 | set; 192 | } 193 | 194 | public bool? IsTransactional 195 | { 196 | get; 197 | set; 198 | } = null; 199 | 200 | public int? CommandTimeoutOverride 201 | { 202 | get; 203 | set; 204 | } = null; 205 | } 206 | 207 | /// 208 | /// Settings applicable to the request to save to the database. 209 | /// 210 | public sealed class DBSaveSettings 211 | { 212 | public IEnumerable? TypesWithChanges 213 | { 214 | get; 215 | set; 216 | } 217 | 218 | public int MaxDegreeOfParallelism 219 | { 220 | get; 221 | set; 222 | } = Globals.DefaultDBSaveDOP; 223 | 224 | public enum Operations 225 | { 226 | Insert = 1, 227 | Update = 2, 228 | Delete = 3, 229 | All = 7 230 | } 231 | 232 | [Obsolete("Use UserProperties instead.")] 233 | public object? UserPayload 234 | { 235 | get; 236 | set; 237 | } = null; 238 | 239 | public bool ConsiderBagPropertiesOnSave 240 | { 241 | get; 242 | set; 243 | } = Globals.ConsiderBagPropertiesOnSave; 244 | 245 | public ValidationErrorCode? ValidationChecksOnSave 246 | { 247 | get; 248 | set; 249 | } = Globals.ValidationChecksOnSave; 250 | 251 | public bool? ValidationFailureIsException 252 | { 253 | get; 254 | set; 255 | } = Globals.ValidationFailureIsException; 256 | 257 | public Dictionary UserProperties 258 | { 259 | get; 260 | set; 261 | } = []; 262 | 263 | public bool? UseAsyncSave 264 | { 265 | get; 266 | set; 267 | } = null; 268 | 269 | public bool? AsyncCacheUpdates 270 | { 271 | get; 272 | set; 273 | } = null; 274 | 275 | public object? RootObject 276 | { 277 | get; 278 | set; 279 | } = null; 280 | 281 | public Type? LimitToSingleType 282 | { 283 | get; 284 | set; 285 | } = null; 286 | 287 | public bool IncludeRootChildren 288 | { 289 | get; 290 | set; 291 | } = true; 292 | 293 | public string? EntityPersistName 294 | { 295 | get; 296 | set; 297 | } = null; 298 | 299 | public Type? EntityPersistType 300 | { 301 | get; 302 | set; 303 | } = null; 304 | 305 | public string? LastUpdatedBy 306 | { 307 | get; 308 | set; 309 | } = null; 310 | 311 | public bool IncludeRootParents 312 | { 313 | get; 314 | set; 315 | } = true; 316 | 317 | public Operations AllowedOperations 318 | { 319 | get; 320 | set; 321 | } = Operations.All; 322 | 323 | public BulkRules BulkInsertRules 324 | { 325 | get; 326 | set; 327 | } = Globals.DefaultBulkInsertRules; 328 | 329 | private IList _bulkInsertTypes = []; 330 | 331 | public IList BulkInsertTypes 332 | { 333 | get 334 | { 335 | return _bulkInsertTypes; 336 | } 337 | set 338 | { 339 | if (value.Count > 0) 340 | { 341 | BulkInsertRules = BulkRules.ByType; 342 | } 343 | 344 | _bulkInsertTypes = value; 345 | } 346 | } 347 | 348 | public int BulkInsertMinimumRows 349 | { 350 | get; 351 | set; 352 | } = 100000; 353 | 354 | public DBSaveSettings UseBulkInsertTypes(params Type[] types) 355 | { 356 | BulkInsertTypes = types; 357 | return this; 358 | } 359 | 360 | public Type? IgnoreObjectType 361 | { 362 | get; 363 | set; 364 | } = null; 365 | 366 | public IEnumerable? SourceList 367 | { 368 | get; 369 | set; 370 | } = null; 371 | 372 | public Func? RowSavePreview 373 | { 374 | get; 375 | set; 376 | } = null; 377 | 378 | public bool? DeferAcceptChanges 379 | { 380 | get; 381 | set; 382 | } = null; 383 | 384 | public bool NoAcceptChanges 385 | { 386 | get; 387 | set; 388 | } = false; 389 | } 390 | 391 | public sealed class PortableSerializationOptions 392 | { 393 | public PortableSerializationOptions() 394 | { 395 | } 396 | 397 | public PortableSerializationOptions(params string[] cols) 398 | { 399 | ExcludeColumns = cols; 400 | } 401 | 402 | public SerializationMode? Mode 403 | { 404 | get; 405 | set; 406 | } = null; 407 | 408 | public IEnumerable? IncludeColumns 409 | { 410 | get; 411 | set; 412 | } = null; 413 | 414 | public IEnumerable? ExcludeColumns 415 | { 416 | get; 417 | set; 418 | } = null; 419 | 420 | public bool? AreColumnsExcluded 421 | { 422 | get; 423 | set; 424 | } = null; 425 | 426 | public bool? ExcludeAudit 427 | { 428 | get; 429 | set; 430 | } = null; 431 | 432 | public bool? OnlyWriteable 433 | { 434 | get; 435 | set; 436 | } = null; 437 | 438 | public bool? IncludeExtended 439 | { 440 | get; 441 | set; 442 | } = null; 443 | 444 | public int? ExtendedPropertySampleSize 445 | { 446 | get; 447 | set; 448 | } = null; 449 | 450 | public DateConversionMode? ConvertDates 451 | { 452 | get; 453 | set; 454 | } = null; 455 | 456 | public Func? SortSpec 457 | { 458 | get; 459 | set; 460 | } 461 | 462 | public Func? FilterSpec 463 | { 464 | get; 465 | set; 466 | } 467 | } 468 | } 469 | -------------------------------------------------------------------------------- /CodexMicroORM.Core/CodexMicroORM.Core.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0;net462;net8.0;net9.0 5 | CodexMicroORM.Core 6 | 1.3.0 7 | CodeX Enterprises LLC 8 | CodeX Enterprises LLC 9 | CodexMicroORM.Core 10 | Copyright (c) 2024, All Rights Reserved 11 | ORM microORM entity-framework database object-mapping micro-ORM entity data-access dataset linq-to-sql 12 | true 13 | true 14 | 15 | https://github.com/codexguy/CodexMicroORM 16 | A high performance, low code alternative to ORM's such as Entity Framework, offers intelligent database mapping to your existing CLR objects with minimal effort. Visit "Design Goals" on GitHub to see more rationale and guidance. 17 | 18 | xSkrape.ico 19 | See: https://github.com/codexguy/CodexMicroORM 20 | 1.3.0.0 21 | 1.3.0.0 22 | Apache-2.0 23 | xSkrapeIcon.jpg 24 | latest 25 | 26 | 27 | 28 | 1701;1702;IDE0008;IDE0058;IDE0047;IDE0060;VSSpell001 29 | 30 | 31 | 1701;1702;IDE0008;IDE0058;IDE0047;IDE0060;VSSpell001;CS0162 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | True 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /CodexMicroORM.Core/DatabaseProviders/MSSQL/MSSQLConnection.cs: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Copyright 2021 CodeX Enterprises LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | Major Changes: 17 | 12/2017 0.2 Initial release (Joel Champagne) 18 | ***********************************************************************/ 19 | #nullable enable 20 | using System; 21 | using Microsoft.Data.SqlClient; 22 | using System.Threading; 23 | using CodexMicroORM.Core; 24 | 25 | namespace CodexMicroORM.Providers 26 | { 27 | /// 28 | /// Implements the functionality expected by CEF for dealing with database connections, for MS SQL Server. 29 | /// 30 | public class MSSQLConnection : IDBProviderConnection 31 | { 32 | #if DEBUG 33 | private readonly string _id = Guid.NewGuid().ToString(); 34 | 35 | public string ID() 36 | { 37 | return _id; 38 | } 39 | #else 40 | public string ID() 41 | { 42 | return string.Empty; 43 | } 44 | #endif 45 | 46 | private int _working = 0; 47 | private readonly object _worklock = new(); 48 | 49 | internal MSSQLConnection(SqlConnection conn, SqlTransaction? tx) 50 | { 51 | CurrentConnection = conn; 52 | CurrentTransaction = tx; 53 | } 54 | 55 | public void IncrementWorking() 56 | { 57 | lock (_worklock) 58 | { 59 | ++_working; 60 | } 61 | } 62 | 63 | public void DecrementWorking() 64 | { 65 | lock (_worklock) 66 | { 67 | if (_working > 0) 68 | { 69 | --_working; 70 | } 71 | } 72 | } 73 | 74 | public bool IsOpen() 75 | { 76 | return CurrentConnection != null && CurrentConnection.State == System.Data.ConnectionState.Open; 77 | } 78 | 79 | public void DeepReset() 80 | { 81 | SqlConnection.ClearAllPools(); 82 | } 83 | 84 | public bool IsWorking() 85 | { 86 | lock (_worklock) 87 | { 88 | if (_working > 0) 89 | { 90 | return true; 91 | } 92 | } 93 | 94 | var ccs = CurrentConnection?.State; 95 | return ccs.HasValue && (ccs == System.Data.ConnectionState.Executing || ccs == System.Data.ConnectionState.Fetching || ccs == System.Data.ConnectionState.Connecting); 96 | } 97 | 98 | public SqlConnection? CurrentConnection { get; private set; } 99 | 100 | public SqlTransaction? CurrentTransaction { get; private set; } 101 | 102 | public void Commit() 103 | { 104 | if (CurrentTransaction != null) 105 | { 106 | CurrentTransaction.Commit(); 107 | CurrentTransaction = null; 108 | } 109 | } 110 | 111 | public void Rollback() 112 | { 113 | if (CurrentTransaction != null) 114 | { 115 | CurrentTransaction.Rollback(); 116 | CurrentTransaction = null; 117 | } 118 | } 119 | 120 | #region IDisposable Support 121 | protected virtual void Dispose(bool disposing) 122 | { 123 | // If it's working, give it a few seconds to see if can finish 124 | for (int i = 1; IsWorking() && i <= 300; ++i) 125 | { 126 | Thread.Sleep(10); 127 | } 128 | 129 | if (!IsWorking()) 130 | { 131 | if (CurrentTransaction != null) 132 | { 133 | CurrentTransaction.Dispose(); 134 | CurrentTransaction = null; 135 | } 136 | 137 | if (CurrentConnection != null) 138 | { 139 | CurrentConnection.Close(); 140 | CurrentConnection.Dispose(); 141 | CurrentConnection = null; 142 | } 143 | } 144 | else 145 | { 146 | //#if DEBUG 147 | // if (System.Diagnostics.Debugger.IsAttached) 148 | // { 149 | // System.Diagnostics.Debugger.Break(); 150 | // } 151 | //#endif 152 | CurrentTransaction = null; 153 | CurrentConnection = null; 154 | } 155 | } 156 | 157 | public void Dispose() 158 | { 159 | Dispose(true); 160 | } 161 | 162 | #endregion 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /CodexMicroORM.Core/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | // This file is used by Code Analysis to maintain SuppressMessage 2 | // attributes that are applied to this project. 3 | // Project-level suppressions either have no target or are given 4 | // a specific target and scoped to a namespace, type, member, etc. 5 | 6 | using System.Diagnostics.CodeAnalysis; 7 | 8 | [assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Code checked", Scope = "module")] 9 | [assembly: SuppressMessage("Usage", "CA2235:Mark all non-serializable fields", Justification = "Behavior Checked", Scope = "module")] 10 | [assembly: SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "Design Choice", Scope = "module")] 11 | -------------------------------------------------------------------------------- /CodexMicroORM.Core/ListServices/Infrastructure/InMemIndexes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using CodexMicroORM.Core.Collections; 5 | using CodexMicroORM.Core.Helper; 6 | #nullable enable 7 | 8 | namespace CodexMicroORM.Core.Services 9 | { 10 | internal class EqualityHashIndex : ICEFDataIndex, ICEFDataIndex where TO : class, new() 11 | { 12 | private readonly ICEFIndexed _source; 13 | private readonly string _propName; 14 | private readonly TO[] _empty = []; 15 | private readonly SlimConcurrentDictionary, ICollection> _data = new(); 16 | 17 | public EqualityHashIndex(ICEFIndexed source, string propName) 18 | { 19 | _source = source; 20 | _propName = propName; 21 | 22 | foreach (TO i in (IEnumerable)_source) 23 | { 24 | Add(i); 25 | } 26 | } 27 | 28 | public void ClearAll() 29 | { 30 | _data.Clear(); 31 | } 32 | 33 | private FieldWrapper GetFieldWrapper(TO obj) 34 | { 35 | var (readable, value) = obj.FastPropertyReadableWithValue(_propName); 36 | 37 | object? v; 38 | 39 | if (readable) 40 | { 41 | v = value; 42 | } 43 | else 44 | { 45 | v = obj.MustInfraWrap().GetValue(_propName); 46 | } 47 | 48 | if (v != null && v.GetType().IsEnum) 49 | { 50 | var tpbt = Nullable.GetUnderlyingType(typeof(TP)) ?? throw new InvalidOperationException("Unable to get underlying type."); 51 | v = Convert.ChangeType(v, tpbt); 52 | } 53 | 54 | return new FieldWrapper((TP?)(v)); 55 | } 56 | 57 | public void UpdateKey(object? oldval, object? newval, object row) 58 | { 59 | if (row is not TO) 60 | { 61 | throw new ArgumentException("Row is not a valid type."); 62 | } 63 | 64 | var oldvalw = new FieldWrapper((TP?)oldval); 65 | 66 | _data.TryGetValue(oldvalw, out var items); 67 | 68 | if (items != null) 69 | { 70 | lock (items) 71 | { 72 | items.Remove((TO)row); 73 | 74 | if (!items.Any()) 75 | { 76 | _data.Remove(oldvalw); 77 | } 78 | } 79 | } 80 | 81 | var newvalw = new FieldWrapper((TP?)newval); 82 | 83 | _data.TryGetValue(newvalw, out items); 84 | 85 | if (items == null) 86 | { 87 | items = new HashSet(); 88 | _data[newvalw] = items; 89 | } 90 | 91 | lock (items) 92 | { 93 | items.Add((TO)row); 94 | } 95 | } 96 | 97 | public void Remove(TO obj) 98 | { 99 | if (obj == null) 100 | throw new ArgumentNullException(nameof(obj)); 101 | 102 | var val = GetFieldWrapper(obj); 103 | 104 | _data.TryGetValue(val, out var items); 105 | 106 | if (items != null) 107 | { 108 | lock (items) 109 | { 110 | items.Remove(obj); 111 | } 112 | 113 | if (!items.Any()) 114 | { 115 | _data.Remove(val); 116 | } 117 | } 118 | } 119 | 120 | public void Add(TO obj) 121 | { 122 | if (obj == null) 123 | throw new ArgumentNullException(nameof(obj)); 124 | 125 | var val = GetFieldWrapper(obj); 126 | 127 | if (_data.TryGetValue(val, out var v)) 128 | { 129 | lock (v) 130 | { 131 | v.Add(obj); 132 | } 133 | } 134 | else 135 | { 136 | HashSet items = new() 137 | { 138 | obj 139 | }; 140 | 141 | _data[val] = items; 142 | } 143 | } 144 | 145 | public IEnumerable GetEqualItems(TP value) 146 | { 147 | _data.TryGetValue(new FieldWrapper(value), out var list); 148 | return list ?? _empty; 149 | } 150 | 151 | public IEnumerable GetEqualItems(object? value) 152 | { 153 | if (typeof(TP) == typeof(byte?) && value != null && value.GetType() == typeof(int)) 154 | { 155 | value = Convert.ToByte(value); 156 | } 157 | 158 | if (typeof(TP) == typeof(OnlyDate?) && value != null && value.GetType() == typeof(DateTime)) 159 | { 160 | value = new OnlyDate((DateTime)value); 161 | } 162 | 163 | var v = (TP?)value; 164 | _data.TryGetValue(new FieldWrapper(v), out var list); 165 | return list ?? _empty; 166 | } 167 | 168 | public bool OnlyEqualitySupport() 169 | { 170 | return true; 171 | } 172 | } 173 | 174 | internal class ComparisonSortedIndex : ICEFDataIndex, ICEFDataIndex, ICEFComparisonDataIndex where TO : class, new() 175 | { 176 | private readonly ICEFIndexed _source; 177 | private readonly string _propName; 178 | private readonly TO[] _empty = []; 179 | private readonly C5.TreeDictionary, ICollection> _data = new(); 180 | private readonly RWLockInfo _lock = new(); 181 | 182 | public ComparisonSortedIndex(ICEFIndexed source, string propName) 183 | { 184 | _source = source; 185 | _propName = propName; 186 | 187 | foreach (TO i in (IEnumerable)_source) 188 | { 189 | Add(i); 190 | } 191 | } 192 | 193 | public void ClearAll() 194 | { 195 | _data.Clear(); 196 | } 197 | 198 | public void UpdateKey(object? oldval, object? newval, object row) 199 | { 200 | if (row is not TO) 201 | { 202 | throw new ArgumentException("Row is not a valid type."); 203 | } 204 | 205 | var oldvalw = new FieldWrapper((TP?)oldval); 206 | 207 | using (new WriterLock(_lock)) 208 | { 209 | if (_data.Contains(oldvalw)) 210 | { 211 | var items = _data[oldvalw]; 212 | 213 | if (items != null) 214 | { 215 | items.Remove((TO)row); 216 | 217 | if (!items.Any()) 218 | { 219 | _data.Remove(oldvalw); 220 | } 221 | } 222 | } 223 | 224 | var newvalw = new FieldWrapper((TP?)newval); 225 | 226 | if (_data.Contains(newvalw)) 227 | { 228 | _data[newvalw].Add((TO)row); 229 | } 230 | else 231 | { 232 | HashSet items = new() 233 | { 234 | (TO)row 235 | }; 236 | _data[newvalw] = items; 237 | } 238 | } 239 | } 240 | 241 | public void Remove(TO obj) 242 | { 243 | if (obj == null) 244 | throw new ArgumentNullException("obj"); 245 | 246 | var val = GetFieldWrapper(obj); 247 | 248 | using (new WriterLock(_lock)) 249 | { 250 | if (_data.Contains(val)) 251 | { 252 | var items = _data[val]; 253 | 254 | if (items != null) 255 | { 256 | items.Remove(obj); 257 | 258 | if (!items.Any()) 259 | { 260 | _data.Remove(val); 261 | } 262 | } 263 | } 264 | } 265 | } 266 | 267 | private FieldWrapper GetFieldWrapper(TO obj) 268 | { 269 | var (readable, value) = obj.FastPropertyReadableWithValue(_propName); 270 | 271 | object? v; 272 | 273 | if (readable) 274 | { 275 | v = value; 276 | } 277 | else 278 | { 279 | v = obj.MustInfraWrap().GetValue(_propName); 280 | } 281 | 282 | if (v != null && v.GetType().IsEnum) 283 | { 284 | v = Activator.CreateInstance(typeof(TP), v); 285 | } 286 | 287 | return new FieldWrapper((TP?)(v)); 288 | } 289 | 290 | public void Add(TO obj) 291 | { 292 | if (obj == null) 293 | throw new ArgumentNullException("obj"); 294 | 295 | var val = GetFieldWrapper(obj); 296 | 297 | using (new WriterLock(_lock)) 298 | { 299 | if (_data.Contains(val)) 300 | { 301 | _data[val].Add(obj); 302 | } 303 | else 304 | { 305 | HashSet items = new() 306 | { 307 | obj 308 | }; 309 | _data[val] = items; 310 | } 311 | } 312 | } 313 | 314 | public IEnumerable GetEqualItems(TP? value) 315 | { 316 | var key = new FieldWrapper((TP?)value.TypeFixup(typeof(TP))); 317 | 318 | using (new ReaderLock(_lock)) 319 | { 320 | if (_data.Contains(key)) 321 | { 322 | return _data[key]; 323 | } 324 | } 325 | 326 | return _empty; 327 | } 328 | 329 | public IEnumerable GetEqualItems(object? value) 330 | { 331 | var key = new FieldWrapper((TP?)value.TypeFixup(typeof(TP))); 332 | 333 | using (new ReaderLock(_lock)) 334 | { 335 | if (_data.Contains(key)) 336 | { 337 | return _data[key]; 338 | } 339 | } 340 | 341 | return _empty; 342 | } 343 | 344 | public IEnumerable GetGreaterThanItems(object? value) 345 | { 346 | using (new ReaderLock(_lock)) 347 | { 348 | return _data.RangeFrom(new FieldWrapper((TP?)value.TypeFixup(typeof(TP)))).SelectMany((p) => p.Value).Except(GetEqualItems(value)); 349 | } 350 | } 351 | 352 | public IEnumerable GetLessThanItems(object? value) 353 | { 354 | using (new ReaderLock(_lock)) 355 | { 356 | return _data.RangeTo(new FieldWrapper((TP?)value.TypeFixup(typeof(TP)))).SelectMany((p) => p.Value); 357 | } 358 | } 359 | 360 | public IEnumerable GetGreaterThanEqualItems(object? value) 361 | { 362 | using (new ReaderLock(_lock)) 363 | { 364 | return _data.RangeFrom(new FieldWrapper((TP?)value.TypeFixup(typeof(TP)))).SelectMany((p) => p.Value); 365 | } 366 | } 367 | 368 | public IEnumerable GetLessThanEqualItems(object? value) 369 | { 370 | using (new ReaderLock(_lock)) 371 | { 372 | return _data.RangeTo(new FieldWrapper((TP?)value.TypeFixup(typeof(TP)))).SelectMany((p) => p.Value).Union(GetEqualItems(value)); 373 | } 374 | } 375 | 376 | public bool OnlyEqualitySupport() 377 | { 378 | return false; 379 | } 380 | } 381 | } 382 | -------------------------------------------------------------------------------- /CodexMicroORM.Core/ObjectServices/Audit.cs: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Copyright 2024 CodeX Enterprises LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | Major Changes: 17 | 12/2017 0.2 Initial release (Joel Champagne) 18 | ***********************************************************************/ 19 | #nullable enable 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Data; 23 | 24 | namespace CodexMicroORM.Core.Services 25 | { 26 | public class AuditService : ICEFAuditHost 27 | { 28 | private readonly static HashSet _canUseBagProp = []; 29 | 30 | public static void RegisterCanUseBagPropertyForAudit(Type t) 31 | { 32 | _canUseBagProp.Add(t.Name); 33 | } 34 | 35 | public static void RegisterCanUseBagPropertyForAudit(string typeName) 36 | { 37 | _canUseBagProp.Add(typeName); 38 | } 39 | 40 | 41 | public Func GetLastUpdatedBy 42 | { 43 | get; 44 | set; 45 | } = () => { return Globals.GetCurrentUser(); }; 46 | 47 | public Func GetLastUpdatedDate 48 | { 49 | get; 50 | set; 51 | } = () => { return DateTime.UtcNow; }; 52 | 53 | public string? IsDeletedField 54 | { 55 | get; 56 | set; 57 | } = "IsDeleted"; 58 | 59 | public string? LastUpdatedByField 60 | { 61 | get; 62 | set; 63 | } = "LastUpdatedBy"; 64 | 65 | public string? LastUpdatedDateField 66 | { 67 | get; 68 | set; 69 | } = "LastUpdatedDate"; 70 | 71 | public bool IsLastUpdatedByDBAssigned 72 | { 73 | get; 74 | set; 75 | } = false; 76 | 77 | public bool IsLastUpdatedDateDBAssigned 78 | { 79 | get; 80 | set; 81 | } = true; 82 | 83 | public AuditService() 84 | { 85 | } 86 | 87 | public AuditService(Func getLastUpdatedBy) 88 | { 89 | GetLastUpdatedBy = getLastUpdatedBy; 90 | } 91 | 92 | public AuditService(Func getLastUpdatedDate) 93 | { 94 | GetLastUpdatedDate = getLastUpdatedDate; 95 | } 96 | 97 | public AuditService(Func getLastUpdatedBy, Func getLastUpdatedDate) 98 | { 99 | GetLastUpdatedBy = getLastUpdatedBy; 100 | GetLastUpdatedDate = getLastUpdatedDate; 101 | } 102 | 103 | public ICEFInfraWrapper SavePreview(ServiceScope ss, ICEFInfraWrapper saving, ObjectState state, DBSaveSettings settings) 104 | { 105 | // Use of bag can depend on type, registered with audit provider 106 | bool? canUseBag = null; 107 | 108 | void SetCanUseBag() 109 | { 110 | if (!canUseBag.HasValue) 111 | { 112 | canUseBag = _canUseBagProp.Contains(saving.GetBaseType().Name); 113 | } 114 | } 115 | 116 | if (!IsLastUpdatedByDBAssigned && !string.IsNullOrEmpty(LastUpdatedByField)) 117 | { 118 | SetCanUseBag(); 119 | saving.SetValue(LastUpdatedByField!, settings?.LastUpdatedBy ?? (ss.Settings.GetLastUpdatedByChanged ? ss.Settings.GetLastUpdatedBy : GetLastUpdatedBy).Invoke(), canUseBag: canUseBag!.Value); 120 | } 121 | 122 | if (!IsLastUpdatedDateDBAssigned && !string.IsNullOrEmpty(LastUpdatedDateField)) 123 | { 124 | SetCanUseBag(); 125 | saving.SetValue(LastUpdatedDateField!, GetLastUpdatedDate.Invoke(), canUseBag: canUseBag!.Value); 126 | } 127 | 128 | if (state == ObjectState.Added) 129 | { 130 | SetCanUseBag(); 131 | if (!string.IsNullOrEmpty(IsDeletedField)) 132 | { 133 | saving.SetValue(IsDeletedField!, false, canUseBag: canUseBag!.Value); 134 | } 135 | } 136 | 137 | return saving; 138 | } 139 | 140 | IList? ICEFService.RequiredServices() 141 | { 142 | return null; 143 | } 144 | 145 | Type? ICEFService.IdentifyStateType(object o, ServiceScope ss, bool isNew) 146 | { 147 | return null; 148 | } 149 | 150 | WrappingSupport ICEFService.IdentifyInfraNeeds(object o, object? replaced, ServiceScope ss, bool isNew) 151 | { 152 | if ((!string.IsNullOrEmpty(LastUpdatedByField) && !(replaced ?? o).HasProperty(LastUpdatedByField!)) || 153 | (!string.IsNullOrEmpty(LastUpdatedDateField) && !(replaced ?? o).HasProperty(LastUpdatedDateField!)) || 154 | (!string.IsNullOrEmpty(IsDeletedField) && !(replaced ?? o).HasProperty(IsDeletedField!))) 155 | { 156 | return WrappingSupport.PropertyBag; 157 | } 158 | 159 | return WrappingSupport.None; 160 | } 161 | 162 | void ICEFService.FinishSetup(ServiceScope.TrackedObject to, ServiceScope ss, bool isNew, IDictionary? props, ICEFServiceObjState? state, bool initFromTemplate, RetrievalIdentityMode identityMode) 163 | { 164 | } 165 | 166 | public virtual void Disposing(ServiceScope ss) 167 | { 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /CodexMicroORM.Core/ObjectServices/Infrastructure/ConnectionScope.cs: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Copyright 2024 CodeX Enterprises LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | Major Changes: 17 | 12/2017 0.2 Initial release (Joel Champagne) 18 | ***********************************************************************/ 19 | #nullable enable 20 | using System; 21 | using System.Collections.Concurrent; 22 | using System.Collections.Generic; 23 | using System.Threading; 24 | 25 | namespace CodexMicroORM.Core.Services 26 | { 27 | /// 28 | /// Why separate connection scopes from service scopes? You could for example have a need for a tx that spans service scopes. You could have a need for different connections within the same service scope. 29 | /// A requirement: never penalize someone for forgetting to create a connection scope - we can create one-offs that act like short-lived connections (usually for the duration of a save). 30 | /// 31 | [Serializable] 32 | public class ConnectionScope : IDisposable 33 | { 34 | private IDBProviderConnection? _conn = null; 35 | private bool _canCommit = false; 36 | 37 | [NonSerialized] 38 | private readonly string? _connStringOverride = null; 39 | 40 | public ConnectionScope(bool tx = true, string? connStringOverride = null, int? timeoutOverride = null) 41 | { 42 | if (DBService.DefaultProvider == null) 43 | { 44 | throw new CEFInvalidStateException(InvalidStateType.MissingInit, "Default data provider not set."); 45 | } 46 | 47 | Provider = DBService.DefaultProvider; 48 | _connStringOverride = connStringOverride; 49 | TimeoutOverride = timeoutOverride; 50 | IsTransactional = tx; 51 | } 52 | 53 | #region "Properties" 54 | 55 | #if DEBUG 56 | [NonSerialized] 57 | public string ID = Guid.NewGuid().ToString(); 58 | #endif 59 | 60 | public ConcurrentDictionary LastOutputVariables 61 | { 62 | get; 63 | private set; 64 | } = new ConcurrentDictionary(); 65 | 66 | public bool IsStandalone 67 | { 68 | get; 69 | internal set; 70 | } 71 | 72 | public bool IsTransactional 73 | { 74 | get; 75 | internal set; 76 | } = true; 77 | 78 | public int? TimeoutOverride 79 | { 80 | get; 81 | internal set; 82 | } = null; 83 | 84 | public IDBProvider Provider { get; } 85 | 86 | public bool ContinueOnError 87 | { 88 | get; 89 | set; 90 | } 91 | 92 | public HashSet ToAcceptList 93 | { 94 | get; 95 | set; 96 | } = []; 97 | 98 | public ConcurrentBag<(ICEFInfraWrapper row, ObjectState prevstate, IList<(string name, object? value)> data)> ToRollbackList 99 | { 100 | get; 101 | set; 102 | } = []; 103 | 104 | public IDBProviderConnection CurrentConnection 105 | { 106 | get 107 | { 108 | lock (this) 109 | { 110 | if (_conn != null && _conn.IsOpen()) 111 | { 112 | return _conn; 113 | } 114 | 115 | _conn = Provider.CreateOpenConnection("default", IsTransactional, _connStringOverride, null); 116 | return _conn; 117 | } 118 | } 119 | } 120 | 121 | #endregion 122 | 123 | #region "Methods" 124 | 125 | /// 126 | /// This is not the same as a full Dispose, retaining the accept list, etc. 127 | /// 128 | public void ResetConnection(bool deepreset = false) 129 | { 130 | if (IsTransactional) 131 | { 132 | throw new CEFInvalidStateException(InvalidStateType.BadAction, "Cannot use ResetConnection with IsTransactional is true."); 133 | } 134 | 135 | IDBProviderConnection? conn = null; 136 | 137 | lock (this) 138 | { 139 | conn = _conn; 140 | _conn = null; 141 | } 142 | 143 | if (conn != null) 144 | { 145 | // Avoid disposal while something might be executing against it 146 | while (conn.IsWorking()) 147 | { 148 | Thread.Sleep(5); 149 | } 150 | 151 | conn.IncrementWorking(); 152 | 153 | try 154 | { 155 | conn.Dispose(); 156 | } 157 | finally 158 | { 159 | conn.DecrementWorking(); 160 | } 161 | } 162 | } 163 | 164 | public void CanCommit() 165 | { 166 | _canCommit = true; 167 | } 168 | 169 | public void DoneWork() 170 | { 171 | // If not standalone or not transactional - we don't need to discard connection, keep it alive 172 | if (IsStandalone) 173 | { 174 | if (IsTransactional) 175 | { 176 | CanCommit(); 177 | } 178 | 179 | Dispose(); 180 | } 181 | } 182 | 183 | #endregion 184 | 185 | #region IDisposable Support 186 | 187 | protected virtual void Dispose(bool disposing) 188 | { 189 | Disposing?.Invoke(); 190 | 191 | IDBProviderConnection? conn = null; 192 | 193 | lock (this) 194 | { 195 | conn = _conn; 196 | _conn = null; 197 | } 198 | 199 | if (conn != null) 200 | { 201 | bool isRB = false; 202 | 203 | if (IsTransactional) 204 | { 205 | if (_canCommit) 206 | { 207 | conn.Commit(); 208 | } 209 | else 210 | { 211 | conn.Rollback(); 212 | ToAcceptList = null!; 213 | isRB = true; 214 | } 215 | } 216 | 217 | if (ToAcceptList != null) 218 | { 219 | foreach (var r in ToAcceptList) 220 | { 221 | r.AcceptChanges(); 222 | } 223 | 224 | ToAcceptList = null!; 225 | } 226 | 227 | if (isRB && ToRollbackList != null) 228 | { 229 | foreach (var (row, prevstate, data) in ToRollbackList) 230 | { 231 | foreach (var (name, value) in data) 232 | { 233 | row.SetValue(name, value); 234 | } 235 | 236 | // We also restore original row state 237 | row.SetRowState(prevstate); 238 | } 239 | } 240 | 241 | //CEFDebug.WriteInfo($"Dispose connection: " + conn.ID() + " for " + ID); 242 | conn.Dispose(); 243 | } 244 | 245 | Disposed?.Invoke(); 246 | } 247 | 248 | public void Dispose() 249 | { 250 | Dispose(true); 251 | GC.SuppressFinalize(this); 252 | } 253 | 254 | public Action? Disposed 255 | { 256 | get; 257 | set; 258 | } 259 | 260 | public Action? Disposing 261 | { 262 | get; 263 | set; 264 | } 265 | 266 | #endregion 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /CodexMicroORM.Core/ObjectServices/Infrastructure/DynamicWithAll.cs: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Copyright 2024 CodeX Enterprises LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | Major Changes: 17 | 12/2017 0.2 Initial release (Joel Champagne) 18 | ***********************************************************************/ 19 | #nullable enable 20 | using System; 21 | using System.Collections.Generic; 22 | using System.ComponentModel; 23 | using System.Data; 24 | 25 | namespace CodexMicroORM.Core.Services 26 | { 27 | /// 28 | /// A fully featured wrapper that implements INotifyPropertyChanged, providing change notifications for updates on CLR properties as well as dynamic prop bag properties. 29 | /// 30 | public sealed class DynamicWithAll : DynamicWithValuesBagErrors, INotifyPropertyChanged 31 | { 32 | internal DynamicWithAll(object o, ObjectState irs, IDictionary? props, IDictionary? types) : base(o, irs, props, types) 33 | { 34 | } 35 | 36 | protected override void OnPropertyChanged(string propName, object? oldVal, object? newVal, bool isBag) 37 | { 38 | base.OnPropertyChanged(propName, oldVal, newVal, isBag); 39 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName)); 40 | } 41 | 42 | public override WrappingSupport SupportsWrapping() 43 | { 44 | return WrappingSupport.OriginalValues | WrappingSupport.PropertyBag | WrappingSupport.Notifications | WrappingSupport.DataErrors; 45 | } 46 | 47 | public event PropertyChangedEventHandler? PropertyChanged; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /CodexMicroORM.Core/ObjectServices/Infrastructure/DynamicWithValuesBagErrors.cs: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Copyright 2024 CodeX Enterprises LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | Major Changes: 17 | 02/2018 0.2.4 Initial release (Joel Champagne) 18 | ***********************************************************************/ 19 | #nullable enable 20 | using System; 21 | using System.Collections.Generic; 22 | using System.ComponentModel; 23 | 24 | namespace CodexMicroORM.Core.Services 25 | { 26 | public class DynamicWithValuesBagErrors : DynamicWithValuesAndBag, IDataErrorInfo 27 | { 28 | string IDataErrorInfo.this[string columnName] 29 | { 30 | get 31 | { 32 | return CEF.CurrentValidationService(this).GetPropertyMessages(this, columnName).AsString().message; 33 | } 34 | } 35 | 36 | string IDataErrorInfo.Error 37 | { 38 | get 39 | { 40 | return CEF.CurrentValidationService(this).GetObjectMessage(this).AsString().message; 41 | } 42 | } 43 | 44 | internal DynamicWithValuesBagErrors(object o, ObjectState irs, IDictionary? props, IDictionary? types) : base(o, irs, props, types) 45 | { 46 | } 47 | 48 | public override WrappingSupport SupportsWrapping() 49 | { 50 | return WrappingSupport.OriginalValues | WrappingSupport.PropertyBag | WrappingSupport.DataErrors; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /CodexMicroORM.Core/ObjectServices/Infrastructure/FieldWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | #nullable enable 3 | 4 | namespace CodexMicroORM.Core.Services 5 | { 6 | /// 7 | /// A value type that can wrap both value and reference types in an orthogonal manner, as part of indexing. 8 | /// The main driver is a need to allow us to include null as the key in directionaries, namely indexing dictionaries. 9 | /// 10 | /// 11 | public readonly struct FieldWrapper : IComparable, IComparable 12 | { 13 | private readonly T? _value; 14 | 15 | public FieldWrapper(T? val) 16 | { 17 | if (Globals.IndexedSetCaseInsensitiveStringCompares && typeof(T) == typeof(string) && val != null) 18 | { 19 | _value = (T)((object)val.ToString()!.ToLower()); 20 | } 21 | else 22 | { 23 | _value = val; 24 | } 25 | } 26 | 27 | public T? Value => _value; 28 | 29 | public int CompareTo(object? other) 30 | { 31 | if (other == null && _value == null) 32 | return 0; 33 | 34 | if (other == null) 35 | return 1; 36 | 37 | if (_value == null) 38 | return -1; 39 | 40 | if (other is FieldWrapper wrapper) 41 | other = wrapper.Value; 42 | 43 | if (other is IComparable && _value is IComparable cmp) 44 | return cmp.CompareTo(other); 45 | 46 | return _value.ToString()!.CompareTo(other!.ToString()); 47 | } 48 | 49 | public int CompareTo(T? other) 50 | { 51 | if (other == null && _value == null) 52 | return 0; 53 | 54 | if (other == null) 55 | return 1; 56 | 57 | if (_value == null) 58 | return -1; 59 | 60 | if (other is IComparable && _value is IComparable cmpg) 61 | return cmpg.CompareTo(other); 62 | 63 | if (other is IComparable && _value is IComparable cmp) 64 | return cmp.CompareTo(other); 65 | 66 | return _value.ToString()!.CompareTo(other.ToString()); 67 | } 68 | 69 | public override int GetHashCode() 70 | { 71 | if (!typeof(T).IsValueType && _value == null) 72 | { 73 | return int.MinValue; 74 | } 75 | 76 | if (_value == null) 77 | { 78 | return -1; 79 | } 80 | 81 | return _value.GetHashCode(); 82 | } 83 | 84 | public override bool Equals(object? obj) 85 | { 86 | T? toCompare; 87 | 88 | if (obj is FieldWrapper ot) 89 | { 90 | toCompare = ot.Value; 91 | } 92 | else 93 | { 94 | if (obj is T t) 95 | { 96 | toCompare = t; 97 | } 98 | else 99 | { 100 | return false; 101 | } 102 | } 103 | 104 | if (!typeof(T).IsValueType) 105 | { 106 | if (toCompare == null && _value == null) 107 | { 108 | return true; 109 | } 110 | 111 | if (toCompare == null || _value == null) 112 | { 113 | return false; 114 | } 115 | } 116 | 117 | return toCompare!.Equals(_value); 118 | } 119 | 120 | public static bool operator ==(FieldWrapper left, FieldWrapper right) 121 | { 122 | return left.Equals(right); 123 | } 124 | 125 | public static bool operator !=(FieldWrapper left, FieldWrapper right) 126 | { 127 | return !(left == right); 128 | } 129 | 130 | public static bool operator <(FieldWrapper left, FieldWrapper right) 131 | { 132 | return left.CompareTo(right) < 0; 133 | } 134 | 135 | public static bool operator <=(FieldWrapper left, FieldWrapper right) 136 | { 137 | return left.CompareTo(right) <= 0; 138 | } 139 | 140 | public static bool operator >(FieldWrapper left, FieldWrapper right) 141 | { 142 | return left.CompareTo(right) > 0; 143 | } 144 | 145 | public static bool operator >=(FieldWrapper left, FieldWrapper right) 146 | { 147 | return left.CompareTo(right) >= 0; 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /CodexMicroORM.Core/ObjectServices/Infrastructure/ValidationWrapper.cs: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Copyright 2024 CodeX Enterprises LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | Major Changes: 17 | 02/2018 0.2.4 Initial release (Joel Champagne) 18 | ***********************************************************************/ 19 | #nullable enable 20 | using System.ComponentModel; 21 | 22 | namespace CodexMicroORM.Core.Services 23 | { 24 | public class ValidationWrapper 25 | { 26 | private readonly IDataErrorInfo? _source; 27 | private readonly IDataErrorInfo? _iwsource; 28 | 29 | internal ValidationWrapper(ICEFInfraWrapper iw) 30 | { 31 | // If contained object implements IDataErrorInfo, support that "first" 32 | _source = iw.GetWrappedObject() as IDataErrorInfo; 33 | _iwsource = iw as IDataErrorInfo; 34 | } 35 | 36 | public bool IsValid => string.IsNullOrEmpty(_source?.Error) && string.IsNullOrEmpty(_iwsource?.Error); 37 | 38 | public bool IsPropertyValid(string propName) => string.IsNullOrEmpty(_source?[propName]) && string.IsNullOrEmpty(_iwsource?[propName]); 39 | 40 | public string? Error => _source?.Error ?? _iwsource?.Error; 41 | 42 | public string? PropertyError(string propName) => _source?[propName] ?? _iwsource?[propName]; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /CodexMicroORM.Core/ObjectServices/TypeChildRelationship.cs: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Copyright 2024 CodeX Enterprises LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | Major Changes: 17 | 05/2018 0.6 Moved from key.cs (Joel Champagne) 18 | ***********************************************************************/ 19 | #nullable enable 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Linq; 23 | using System.Text; 24 | using CodexMicroORM.Core.Collections; 25 | 26 | namespace CodexMicroORM.Core.Services 27 | { 28 | public sealed class TypeChildRelationship : ICEFIndexedListItem 29 | { 30 | private string _identity = ""; 31 | 32 | private void SetIdentity() 33 | { 34 | StringBuilder sb = new(128); 35 | sb.Append(ParentType?.Name); 36 | sb.Append(ChildType?.Name); 37 | sb.Append(ChildPropertyName); 38 | sb.Append(ParentPropertyName); 39 | if (ParentKey != null) 40 | { 41 | sb.Append(string.Join("", ParentKey)); 42 | } 43 | if (ChildRoleName != null) 44 | { 45 | sb.Append(string.Join("", ChildRoleName)); 46 | } 47 | _identity = sb.ToString(); 48 | } 49 | 50 | public override int GetHashCode() 51 | { 52 | var h1 = unchecked((uint)_identity.GetHashCode()); 53 | uint rol = (h1 << 5) | (h1 >> 27); 54 | return unchecked((int)(rol + h1)); 55 | } 56 | 57 | public override bool Equals(object? obj) => this._identity.IsSame((obj as TypeChildRelationship)?._identity); 58 | 59 | private Type? _childType; 60 | public Type? ChildType 61 | { 62 | get { return _childType; } 63 | internal set 64 | { 65 | _childType = value; 66 | SetIdentity(); 67 | } 68 | } 69 | 70 | private Type? _parentType; 71 | public Type? ParentType 72 | { 73 | get { return _parentType; } 74 | internal set 75 | { 76 | _parentType = value; 77 | SetIdentity(); 78 | } 79 | } 80 | 81 | private IList? _parentKey; 82 | public IList? ParentKey 83 | { 84 | get { return _parentKey; } 85 | internal set 86 | { 87 | _parentKey = value; 88 | SetIdentity(); 89 | } 90 | } 91 | 92 | private string? _childPropertyName; 93 | public string? ChildPropertyName 94 | { 95 | get { return _childPropertyName; } 96 | internal set 97 | { 98 | _childPropertyName = value; 99 | SetIdentity(); 100 | } 101 | } 102 | 103 | public string? FullParentChildPropertyName 104 | { 105 | get 106 | { 107 | if (!string.IsNullOrEmpty(ChildPropertyName)) 108 | { 109 | return $"{ParentType?.Name}.{ChildPropertyName}"; 110 | } 111 | return null; 112 | } 113 | } 114 | 115 | public string? FullChildParentPropertyName 116 | { 117 | get 118 | { 119 | if (!string.IsNullOrEmpty(ParentPropertyName)) 120 | { 121 | return $"{ChildType?.Name}.{ParentPropertyName}"; 122 | } 123 | return null; 124 | } 125 | } 126 | 127 | private string? _parentPropertyName; 128 | public string? ParentPropertyName 129 | { 130 | get { return _parentPropertyName; } 131 | internal set 132 | { 133 | _parentPropertyName = value; 134 | SetIdentity(); 135 | } 136 | } 137 | 138 | private IList? _childRoleName = null; 139 | public IList? ChildRoleName 140 | { 141 | get 142 | { 143 | return _childRoleName; 144 | } 145 | internal set 146 | { 147 | _childRoleName = value == null ? null : value.Count == 0 ? null : value; 148 | SetIdentity(); 149 | } 150 | } 151 | 152 | public IList ChildResolvedKey 153 | { 154 | get 155 | { 156 | return _childRoleName ?? _parentKey ?? []; 157 | } 158 | } 159 | 160 | public TypeChildRelationship MapsToChildProperty(string propName) 161 | { 162 | ChildPropertyName = propName; 163 | return this; 164 | } 165 | 166 | public TypeChildRelationship MapsToParentProperty(string propName) 167 | { 168 | ParentPropertyName = propName; 169 | return this; 170 | } 171 | 172 | public static TypeChildRelationship Create(params string[] childRoleName) 173 | { 174 | var i = new TypeChildRelationship 175 | { 176 | ChildType = typeof(TC), 177 | ChildRoleName = childRoleName 178 | }; 179 | return i; 180 | } 181 | 182 | public object? GetValue(string propName, bool unwrap) 183 | { 184 | return propName switch 185 | { 186 | nameof(TypeChildRelationship.ChildPropertyName) => ChildPropertyName, 187 | nameof(TypeChildRelationship.ChildRoleName) => ChildRoleName, 188 | nameof(TypeChildRelationship.ChildType) => ChildType, 189 | nameof(TypeChildRelationship.ParentKey) => ParentKey, 190 | nameof(TypeChildRelationship.ParentPropertyName) => ParentPropertyName, 191 | nameof(TypeChildRelationship.ParentType) => ParentType, 192 | nameof(TypeChildRelationship.FullParentChildPropertyName) => FullParentChildPropertyName, 193 | nameof(TypeChildRelationship.FullChildParentPropertyName) => FullChildParentPropertyName, 194 | _ => throw new NotSupportedException("Unsupported property name."), 195 | }; 196 | } 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /CodexMicroORM.Core/Pooling.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Linq; 4 | using System.Threading; 5 | #nullable enable 6 | 7 | namespace CodexMicroORM.Core 8 | { 9 | /// 10 | /// This allows any class to be "pooled" for reuse. This is useful for classes that are expensive to create and/or destroy, OR for classes which are not thread-safe. 11 | /// Usage: wrap in a using statement, and the item will be returned to the pool when disposed. 12 | /// Example: using (var item = new PoolableItem(() => new MyClass())) { ... } 13 | /// The constructor accepts a delegate which will be invoked to create a new instance of the class, when needed. 14 | /// Pool size will automatically grow to MaxItemCount, if set. If MaxItemCount is not set, the pool can grow indefinitely. 15 | /// If pool is exhausted and MaxWaitSeconds is set, the constructor will wait for a slot to open up, up to the specified time, then throw a timeout exception. 16 | /// If MaxLifeMinutes is set, items will be evicted from the pool after that many minutes have passed (checked every minute; should be set prior to first use). 17 | /// Access constructed value using .Item property. 18 | /// 19 | /// 20 | public class PoolableItemWrapper : IDisposable where T : class 21 | { 22 | private readonly static ConcurrentDictionary _items = []; 23 | private static long _runningCount = 0; 24 | private static Timer? _evictor; 25 | 26 | public delegate T CreateItemDelegate(); 27 | 28 | public static int? MaxItemCount { get; set; } 29 | 30 | public static int? MaxLifeMinutes { get; set; } 31 | 32 | public static int? MaxWaitSeconds { get; set; } 33 | 34 | public static int WaitIntervalMs { get; set; } = 5; 35 | 36 | public static int CurrentPoolCount => _items.Count; 37 | 38 | private T? _using; 39 | private bool _disposed = false; 40 | 41 | public T Item => _using ?? throw new InvalidOperationException("Object has been disposed."); 42 | 43 | public PoolableItemWrapper(CreateItemDelegate ci) 44 | { 45 | lock (_items) 46 | { 47 | if (MaxLifeMinutes.HasValue && _evictor == null) 48 | { 49 | _evictor = new Timer((o) => 50 | { 51 | try 52 | { 53 | var now = DateTime.Now; 54 | foreach (var item in _items) 55 | { 56 | if ((now - item.Value).TotalMinutes > MaxLifeMinutes.Value) 57 | { 58 | _items.TryRemove(item.Key, out _); 59 | } 60 | } 61 | } 62 | catch 63 | { 64 | // failure could bring down app 65 | } 66 | }, null, 0, 60000); 67 | } 68 | } 69 | 70 | static T? getfrompool() 71 | { 72 | if (!_items.IsEmpty) 73 | { 74 | var touse = _items.Keys.FirstOrDefault(); 75 | 76 | if (touse != null) 77 | { 78 | if (_items.TryRemove(touse, out _)) 79 | { 80 | return touse; 81 | } 82 | } 83 | } 84 | 85 | return null; 86 | } 87 | 88 | var touse = getfrompool(); 89 | 90 | if (touse == null) 91 | { 92 | if (MaxItemCount.HasValue && _items.Count + Interlocked.Read(ref _runningCount) >= MaxItemCount.Value) 93 | { 94 | if (MaxWaitSeconds.HasValue) 95 | { 96 | var start = DateTime.Now; 97 | while (_items.Count + Interlocked.Read(ref _runningCount) >= MaxItemCount.Value) 98 | { 99 | Thread.Sleep(WaitIntervalMs); 100 | touse = getfrompool(); 101 | 102 | if (touse != null) 103 | { 104 | break; 105 | } 106 | 107 | if ((DateTime.Now - start).TotalSeconds > MaxWaitSeconds.Value) 108 | { 109 | throw new TimeoutException($"Timeout waiting for pool space for type {typeof(T).Name}."); 110 | } 111 | } 112 | } 113 | else 114 | { 115 | throw new InvalidOperationException($"Pool for type {typeof(T).Name} is full."); 116 | } 117 | } 118 | 119 | touse ??= ci(); 120 | } 121 | 122 | _using = touse; 123 | Interlocked.Increment(ref _runningCount); 124 | } 125 | 126 | public void Dispose() 127 | { 128 | if (!_disposed) 129 | { 130 | _disposed = true; 131 | _items.TryAdd(_using!, DateTime.Now); 132 | Interlocked.Decrement(ref _runningCount); 133 | _using = null; 134 | GC.SuppressFinalize(this); 135 | } 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /CodexMicroORM.Core/Properties/PublishProfiles/FolderProfile.pubxml: -------------------------------------------------------------------------------- 1 |  2 | 5 | 6 | 7 | FileSystem 8 | Release 9 | Any CPU 10 | netstandard2.1 11 | bin\Release\netstandard2.1\publish\ 12 | 13 | -------------------------------------------------------------------------------- /CodexMicroORM.Core/Thumbs.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codexguy/CodexMicroORM/ebaa8bc6c8fe4cfabe460329f0a95ddb4644119e/CodexMicroORM.Core/Thumbs.db -------------------------------------------------------------------------------- /CodexMicroORM.Core/xSkrape.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codexguy/CodexMicroORM/ebaa8bc6c8fe4cfabe460329f0a95ddb4644119e/CodexMicroORM.Core/xSkrape.ico -------------------------------------------------------------------------------- /CodexMicroORM.Core/xSkrapeIcon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codexguy/CodexMicroORM/ebaa8bc6c8fe4cfabe460329f0a95ddb4644119e/CodexMicroORM.Core/xSkrapeIcon.jpg -------------------------------------------------------------------------------- /CodexMicroORM.SQLServer/CodexMicroORM.SQLServer.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0;net462;net8.0;net9.0 5 | latest 6 | True 7 | 1.2.0 8 | CodeX Enterprises LLC 9 | CodeX Enterprises LLC 10 | The database provider code to implement support for Microsoft SQL Server using the CodexMicroORM application framework. 11 | Copyright (c) 2024, All Rights Reserved 12 | https://github.com/codexguy/CodexMicroORM 13 | xSkrapeIcon.jpg 14 | ORM microORM entity-framework database object-mapping micro-ORM entity data-access dataset linq-to-sql 15 | See: https://github.com/codexguy/CodexMicroORM 16 | Apache-2.0 17 | True 18 | 19 | 20 | 21 | 1701;1702;IDE0008;IDE0058;IDE0047;IDE0060;VSSpell001 22 | 23 | 24 | 1701;1702;IDE0008;IDE0058;IDE0047;IDE0060;VSSpell001 25 | 26 | 27 | 28 | 29 | True 30 | \ 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /CodexMicroORM.SQLServer/DataAccess/MSSQLConnection.cs: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | Copyright 2024 CodeX Enterprises LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | Major Changes: 17 | 12/2017 0.2 Initial release (Joel Champagne) 18 | ***********************************************************************/ 19 | #nullable enable 20 | using System; 21 | using Microsoft.Data.SqlClient; 22 | using System.Threading; 23 | using CodexMicroORM.Core; 24 | 25 | namespace CodexMicroORM.Providers 26 | { 27 | /// 28 | /// Implements the functionality expected by CEF for dealing with database connections, for MS SQL Server. 29 | /// 30 | public class MSSQLConnection : IDBProviderConnection 31 | { 32 | #if DEBUG 33 | private readonly string _id = Guid.NewGuid().ToString(); 34 | 35 | public string ID() 36 | { 37 | return _id; 38 | } 39 | #else 40 | public string ID() 41 | { 42 | return string.Empty; 43 | } 44 | #endif 45 | 46 | private int _working = 0; 47 | private readonly object _worklock = new(); 48 | 49 | internal MSSQLConnection(SqlConnection conn, SqlTransaction? tx) 50 | { 51 | CurrentConnection = conn; 52 | CurrentTransaction = tx; 53 | } 54 | 55 | public void IncrementWorking() 56 | { 57 | lock (_worklock) 58 | { 59 | ++_working; 60 | } 61 | } 62 | 63 | public void DecrementWorking() 64 | { 65 | lock (_worklock) 66 | { 67 | if (_working > 0) 68 | { 69 | --_working; 70 | } 71 | } 72 | } 73 | 74 | public bool IsOpen() 75 | { 76 | return CurrentConnection != null && CurrentConnection.State == System.Data.ConnectionState.Open; 77 | } 78 | 79 | public void DeepReset() 80 | { 81 | SqlConnection.ClearAllPools(); 82 | } 83 | 84 | public bool IsWorking() 85 | { 86 | lock (_worklock) 87 | { 88 | if (_working > 0) 89 | { 90 | return true; 91 | } 92 | } 93 | 94 | var ccs = CurrentConnection?.State; 95 | return ccs.HasValue && (ccs == System.Data.ConnectionState.Executing || ccs == System.Data.ConnectionState.Fetching || ccs == System.Data.ConnectionState.Connecting); 96 | } 97 | 98 | public SqlConnection? CurrentConnection { get; private set; } 99 | 100 | public SqlTransaction? CurrentTransaction { get; private set; } 101 | 102 | public void Commit() 103 | { 104 | if (CurrentTransaction != null) 105 | { 106 | CurrentTransaction.Commit(); 107 | CurrentTransaction = null; 108 | } 109 | } 110 | 111 | public void Rollback() 112 | { 113 | if (CurrentTransaction != null) 114 | { 115 | CurrentTransaction.Rollback(); 116 | CurrentTransaction = null; 117 | } 118 | } 119 | 120 | #region IDisposable Support 121 | protected virtual void Dispose(bool disposing) 122 | { 123 | // If it's working, give it a few seconds to see if can finish 124 | for (int i = 1; IsWorking() && i <= 300; ++i) 125 | { 126 | Thread.Sleep(10); 127 | } 128 | 129 | if (!IsWorking()) 130 | { 131 | if (CurrentTransaction != null) 132 | { 133 | CurrentTransaction.Dispose(); 134 | CurrentTransaction = null; 135 | } 136 | 137 | if (CurrentConnection != null) 138 | { 139 | CurrentConnection.Close(); 140 | CurrentConnection.Dispose(); 141 | CurrentConnection = null; 142 | } 143 | } 144 | else 145 | { 146 | //#if DEBUG 147 | // if (System.Diagnostics.Debugger.IsAttached) 148 | // { 149 | // System.Diagnostics.Debugger.Break(); 150 | // } 151 | //#endif 152 | CurrentTransaction = null; 153 | CurrentConnection = null; 154 | } 155 | } 156 | 157 | public void Dispose() 158 | { 159 | Dispose(true); 160 | } 161 | 162 | #endregion 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /CodexMicroORM.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31912.275 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodexMicroORM.Core", "CodexMicroORM.Core\CodexMicroORM.Core.csproj", "{0BE418F4-B416-4945-B664-B052B9BF4947}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{27C7F078-1FB3-4164-AEE3-C7F39969C6FD}" 9 | ProjectSection(SolutionItems) = preProject 10 | LICENSE = LICENSE 11 | README.md = README.md 12 | EndProjectSection 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodexMicroORM.CodexV1CompatLayer", "CodexMicroORM.CodexV1CompatLayer\CodexMicroORM.CodexV1CompatLayer\CodexMicroORM.CodexV1CompatLayer.csproj", "{070C267E-E5C0-4570-A65F-F40E772C13F9}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodexMicroORM.BaseTests", "BaseTests\CodexMicroORM.BaseTests.csproj", "{B79D3ADD-D81F-48C9-8873-85CED03A9E8A}" 17 | EndProject 18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodexMicroORM.SQLServer", "CodexMicroORM.SQLServer\CodexMicroORM.SQLServer.csproj", "{8B2BBBDF-5065-4A0E-BD1E-290D549E234C}" 19 | EndProject 20 | Global 21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 22 | Debug|Any CPU = Debug|Any CPU 23 | Release|Any CPU = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 26 | {0BE418F4-B416-4945-B664-B052B9BF4947}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {0BE418F4-B416-4945-B664-B052B9BF4947}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {0BE418F4-B416-4945-B664-B052B9BF4947}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {0BE418F4-B416-4945-B664-B052B9BF4947}.Release|Any CPU.Build.0 = Release|Any CPU 30 | {070C267E-E5C0-4570-A65F-F40E772C13F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {070C267E-E5C0-4570-A65F-F40E772C13F9}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {070C267E-E5C0-4570-A65F-F40E772C13F9}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {070C267E-E5C0-4570-A65F-F40E772C13F9}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {B79D3ADD-D81F-48C9-8873-85CED03A9E8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {B79D3ADD-D81F-48C9-8873-85CED03A9E8A}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {B79D3ADD-D81F-48C9-8873-85CED03A9E8A}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {B79D3ADD-D81F-48C9-8873-85CED03A9E8A}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {8B2BBBDF-5065-4A0E-BD1E-290D549E234C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {8B2BBBDF-5065-4A0E-BD1E-290D549E234C}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {8B2BBBDF-5065-4A0E-BD1E-290D549E234C}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {8B2BBBDF-5065-4A0E-BD1E-290D549E234C}.Release|Any CPU.Build.0 = Release|Any CPU 42 | EndGlobalSection 43 | GlobalSection(SolutionProperties) = preSolution 44 | HideSolutionNode = FALSE 45 | EndGlobalSection 46 | GlobalSection(ExtensibilityGlobals) = postSolution 47 | SolutionGuid = {66DF0B37-0FCC-4010-9A2C-A3C8E000E91A} 48 | EndGlobalSection 49 | GlobalSection(Performance) = preSolution 50 | HasPerformanceSessions = true 51 | EndGlobalSection 52 | GlobalSection(Performance) = preSolution 53 | HasPerformanceSessions = true 54 | EndGlobalSection 55 | GlobalSection(Performance) = preSolution 56 | HasPerformanceSessions = true 57 | EndGlobalSection 58 | GlobalSection(Performance) = preSolution 59 | HasPerformanceSessions = true 60 | EndGlobalSection 61 | GlobalSection(Performance) = preSolution 62 | HasPerformanceSessions = true 63 | EndGlobalSection 64 | GlobalSection(Performance) = preSolution 65 | HasPerformanceSessions = true 66 | EndGlobalSection 67 | GlobalSection(Performance) = preSolution 68 | HasPerformanceSessions = true 69 | EndGlobalSection 70 | GlobalSection(Performance) = preSolution 71 | HasPerformanceSessions = true 72 | EndGlobalSection 73 | GlobalSection(Performance) = preSolution 74 | HasPerformanceSessions = true 75 | EndGlobalSection 76 | GlobalSection(Performance) = preSolution 77 | HasPerformanceSessions = true 78 | EndGlobalSection 79 | GlobalSection(Performance) = preSolution 80 | HasPerformanceSessions = true 81 | EndGlobalSection 82 | GlobalSection(Performance) = preSolution 83 | HasPerformanceSessions = true 84 | EndGlobalSection 85 | GlobalSection(Performance) = preSolution 86 | HasPerformanceSessions = true 87 | EndGlobalSection 88 | GlobalSection(Performance) = preSolution 89 | HasPerformanceSessions = true 90 | EndGlobalSection 91 | GlobalSection(Performance) = preSolution 92 | HasPerformanceSessions = true 93 | EndGlobalSection 94 | GlobalSection(Performance) = preSolution 95 | HasPerformanceSessions = true 96 | EndGlobalSection 97 | GlobalSection(Performance) = preSolution 98 | HasPerformanceSessions = true 99 | EndGlobalSection 100 | GlobalSection(Performance) = preSolution 101 | HasPerformanceSessions = true 102 | EndGlobalSection 103 | GlobalSection(Performance) = preSolution 104 | HasPerformanceSessions = true 105 | EndGlobalSection 106 | GlobalSection(Performance) = preSolution 107 | HasPerformanceSessions = true 108 | EndGlobalSection 109 | GlobalSection(Performance) = preSolution 110 | HasPerformanceSessions = true 111 | EndGlobalSection 112 | GlobalSection(Performance) = preSolution 113 | HasPerformanceSessions = true 114 | EndGlobalSection 115 | GlobalSection(Performance) = preSolution 116 | HasPerformanceSessions = true 117 | EndGlobalSection 118 | GlobalSection(Performance) = preSolution 119 | HasPerformanceSessions = true 120 | EndGlobalSection 121 | GlobalSection(Performance) = preSolution 122 | HasPerformanceSessions = true 123 | EndGlobalSection 124 | GlobalSection(Performance) = preSolution 125 | HasPerformanceSessions = true 126 | EndGlobalSection 127 | GlobalSection(Performance) = preSolution 128 | HasPerformanceSessions = true 129 | EndGlobalSection 130 | GlobalSection(Performance) = preSolution 131 | HasPerformanceSessions = true 132 | EndGlobalSection 133 | GlobalSection(Performance) = preSolution 134 | HasPerformanceSessions = true 135 | EndGlobalSection 136 | GlobalSection(Performance) = preSolution 137 | HasPerformanceSessions = true 138 | EndGlobalSection 139 | GlobalSection(Performance) = preSolution 140 | HasPerformanceSessions = true 141 | EndGlobalSection 142 | GlobalSection(Performance) = preSolution 143 | HasPerformanceSessions = true 144 | EndGlobalSection 145 | GlobalSection(Performance) = preSolution 146 | HasPerformanceSessions = true 147 | EndGlobalSection 148 | GlobalSection(Performance) = preSolution 149 | HasPerformanceSessions = true 150 | EndGlobalSection 151 | GlobalSection(Performance) = preSolution 152 | HasPerformanceSessions = true 153 | EndGlobalSection 154 | EndGlobal 155 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | --------------------------------------------------------------------------------