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