├── .editorconfig
├── .gitignore
├── Artisan.Orm
├── Artisan.Orm.csproj
├── DataReply.cs
├── DataReplyException.cs
├── DataReplyMessage.cs
├── DataReplyState.cs
├── DataTableExtensions.cs
├── INode.cs
├── LICENSE.txt
├── Logo.ico
├── MapperForAttribute.cs
├── MappingManager.cs
├── MergeJoinExtensions.cs
├── NegativeIdentity.cs
├── ObjectRows.cs
├── RepositoryBase.cs
├── SqlCommandExtensions_Async.cs
├── SqlCommandExtensions_Parameters.cs
├── SqlCommandExtensions_Sync.cs
├── SqlDataReaderExtensions_CreateObject.cs
├── SqlDataReaderExtensions_Get.cs
├── SqlDataReaderExtensions_Read.cs
├── StringExtensions.cs
├── TreeExtensions.cs
└── TypeExtensions.cs
├── Artisan.sln
├── Database
├── 1. Tables
│ ├── Records
│ │ ├── 1. GrandRecords.sql
│ │ ├── 2. Records.sql
│ │ ├── 3. ChildRecords.sql
│ │ └── 4. RecordTypes.sql
│ └── Users
│ │ ├── 1. Users.sql
│ │ ├── 2. Roles.sql
│ │ ├── 3. UserRoles.sql
│ │ └── 4. Folders.sql
├── 2. Views
│ ├── vwChildRecords.sql
│ ├── vwGrandRecords.sql
│ ├── vwRecords.sql
│ ├── vwRecordsWithTypes.sql
│ └── vwUsers.sql
├── 3. Functions
│ ├── GetHidCode.sql
│ ├── GetUserRoleIds.sql
│ └── Utility Functions
│ │ ├── GetErrorMessage.sql
│ │ ├── SplitIntIds.sql
│ │ ├── SplitSmallIntIds.sql
│ │ └── SplitTinyIntIds.sql
├── 4. Types
│ ├── ChildRecordTableType.sql
│ ├── Common
│ │ ├── BigIntIdTableType.sql
│ │ ├── DataMessageTableType.sql
│ │ ├── IntIdTableType.sql
│ │ ├── SmallIntIdTableType.sql
│ │ └── TinyIntIdTableType.sql
│ ├── FolderTableType.sql
│ ├── GrandRecordTableType.sql
│ ├── RecordTableType.sql
│ └── UserTableType.sql
├── 5. Procedures
│ ├── Folders
│ │ ├── DeleteFolder.sql
│ │ ├── FindFoldersWithParents.sql
│ │ ├── GenerateFolders.sql
│ │ ├── GetFolderById.sql
│ │ ├── GetFolderParents.sql
│ │ ├── GetFolderWithSubFolders.sql
│ │ ├── GetImmediateSubFolders.sql
│ │ ├── GetNextSiblingFolder.sql
│ │ ├── GetPreviousSiblingFolder.sql
│ │ ├── GetUserRootFolder.sql
│ │ ├── ReculcSubFolderHids.sql
│ │ └── SaveFolder.sql
│ ├── GrandRecords
│ │ ├── GetGrandRecordById.sql
│ │ ├── GetGrandRecords.sql
│ │ └── SaveGrandRecords.sql
│ ├── Records
│ │ ├── GetRecordById.sql
│ │ ├── GetRecords.sql
│ │ └── SaveRecords.sql
│ ├── SqlParameters
│ │ ├── GetDateTimeParams.sql
│ │ ├── GetFractionalNumberParams.sql
│ │ ├── GetGuidAndRowVersionParams.sql
│ │ ├── GetStringParams.sql
│ │ └── GetWholeNumberParams.sql
│ └── Users
│ │ ├── DeleteUser.sql
│ │ ├── GetUserById.sql
│ │ ├── GetUsers.sql
│ │ ├── SaveUser.sql
│ │ └── SaveUsers.sql
├── 6. Post-Deployment
│ ├── 1. Users.sql
│ ├── 2. Roles.sql
│ ├── 3. UserRoles.sql
│ ├── 4. RecordTypes.sql
│ ├── 5. Records.sql
│ ├── 6. Sequence reset.sql
│ └── PostDeployment.sql
├── Artisan.publish.xml
├── Database.sqlproj
└── Utility Scripts
│ ├── Artisan Code Generation.sql
│ └── Generate MERGE SQL.sql
├── Logo.png
├── README.md
└── Tests
├── AppSettings.cs
├── DAL
├── ByteArrayConverter.cs
├── Folders
│ ├── Models
│ │ └── Folder.cs
│ └── Repository.cs
├── GrandRecords
│ ├── Models
│ │ ├── ChildRecord.cs
│ │ ├── GrandRecord.cs
│ │ ├── Record.cs
│ │ └── RecordType.cs
│ └── Repository.cs
├── Records
│ ├── Models
│ │ └── Record.cs
│ └── Repository.cs
└── Users
│ ├── Models
│ ├── Role.cs
│ └── User.cs
│ └── Repository.cs
├── DataServices
├── DataServiceBase.cs
└── UserDataService.cs
├── Tests.csproj
├── Tests
├── CommandReadTest.cs
├── EspecialRepositoryTest.cs
├── FolderTests.cs
├── GrandRecordRepositoryTest.cs
├── ReadDictionaryTest.cs
├── RecordRepositoryTest.cs
├── SqlParemeterTest.cs
├── UserDataServiceTest.cs
└── UserRepositoryTest.cs
└── appsettings.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.{cs,sql}]
2 | charset = utf-8
3 | indent_style = tab
4 | indent_size = 4
5 | trim_trailing_whitespace = true
6 |
--------------------------------------------------------------------------------
/.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 | artifacts/
46 |
47 | *_i.c
48 | *_p.c
49 | *_i.h
50 | *.ilk
51 | *.meta
52 | *.obj
53 | *.pch
54 | *.pdb
55 | *.pgc
56 | *.pgd
57 | *.rsp
58 | *.sbr
59 | *.tlb
60 | *.tli
61 | *.tlh
62 | *.tmp
63 | *.tmp_proj
64 | *.log
65 | *.vspscc
66 | *.vssscc
67 | .builds
68 | *.pidb
69 | *.svclog
70 | *.scc
71 |
72 | *.jfm
73 |
74 | # Chutzpah Test files
75 | _Chutzpah*
76 |
77 | # Visual C++ cache files
78 | ipch/
79 | *.aps
80 | *.ncb
81 | *.opendb
82 | *.opensdf
83 | *.sdf
84 | *.cachefile
85 | *.VC.db
86 | *.VC.VC.opendb
87 |
88 | # Visual Studio profiler
89 | *.psess
90 | *.vsp
91 | *.vspx
92 | *.sap
93 |
94 | # TFS 2012 Local Workspace
95 | $tf/
96 |
97 | # Guidance Automation Toolkit
98 | *.gpState
99 |
100 | # ReSharper is a .NET coding add-in
101 | _ReSharper*/
102 | *.[Rr]e[Ss]harper
103 | *.DotSettings.user
104 |
105 | # JustCode is a .NET coding add-in
106 | .JustCode
107 |
108 | # TeamCity is a build add-in
109 | _TeamCity*
110 |
111 | # DotCover is a Code Coverage Tool
112 | *.dotCover
113 |
114 | # NCrunch
115 | _NCrunch_*
116 | .*crunch*.local.xml
117 | nCrunchTemp_*
118 |
119 | # MightyMoose
120 | *.mm.*
121 | AutoTest.Net/
122 |
123 | # Web workbench (sass)
124 | .sass-cache/
125 |
126 | # Installshield output folder
127 | [Ee]xpress/
128 |
129 | # DocProject is a documentation generator add-in
130 | DocProject/buildhelp/
131 | DocProject/Help/*.HxT
132 | DocProject/Help/*.HxC
133 | DocProject/Help/*.hhc
134 | DocProject/Help/*.hhk
135 | DocProject/Help/*.hhp
136 | DocProject/Help/Html2
137 | DocProject/Help/html
138 |
139 | # Click-Once directory
140 | publish/
141 |
142 | # Publish Web Output
143 | # *.[Pp]ublish.xml
144 | *.azurePubxml
145 | # TODO: Comment the next line if you want to checkin your web deploy settings
146 | # but database connection strings (with potential passwords) will be unencrypted
147 | *.pubxml
148 | *.publishproj
149 |
150 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
151 | # checkin your Azure Web App publish settings, but sensitive information contained
152 | # in these scripts will be unencrypted
153 | PublishScripts/
154 |
155 | # NuGet Packages
156 | *.nupkg
157 | *.nuspec
158 | # The packages folder can be ignored because of Package Restore
159 | **/packages/*
160 | # except build/, which is used as an MSBuild target.
161 | !**/packages/build/
162 | # Uncomment if necessary however generally it will be regenerated when needed
163 | #!**/packages/repositories.config
164 | # NuGet v3's project.json files produces more ignoreable files
165 | *.nuget.props
166 | *.nuget.targets
167 |
168 | # Microsoft Azure Build Output
169 | csx/
170 | *.build.csdef
171 |
172 | # Microsoft Azure Emulator
173 | ecf/
174 | rcf/
175 |
176 | # Windows Store app package directories and files
177 | AppPackages/
178 | BundleArtifacts/
179 | Package.StoreAssociation.xml
180 | _pkginfo.txt
181 |
182 | # Visual Studio cache files
183 | # files ending in .cache can be ignored
184 | *.[Cc]ache
185 | # but keep track of directories ending in .cache
186 | !*.[Cc]ache/
187 |
188 | # Others
189 | ClientBin/
190 | ~$*
191 | *~
192 | *.dbmdl
193 | *.dbproj.schemaview
194 | *.pfx
195 | *.publishsettings
196 | node_modules/
197 | orleans.codegen.cs
198 |
199 | # Since there are multiple workflows, uncomment next line to ignore bower_components
200 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
201 | #bower_components/
202 |
203 | # RIA/Silverlight projects
204 | Generated_Code/
205 |
206 | # Backup & report files from converting an old project file
207 | # to a newer Visual Studio version. Backup files are not needed,
208 | # because we have git ;-)
209 | _UpgradeReport_Files/
210 | Backup*/
211 | UpgradeLog*.XML
212 | UpgradeLog*.htm
213 |
214 | # SQL Server files
215 | *.mdf
216 | *.ldf
217 |
218 | # Business Intelligence projects
219 | *.rdl.data
220 | *.bim.layout
221 | *.bim_*.settings
222 |
223 | # Microsoft Fakes
224 | FakesAssemblies/
225 |
226 | # GhostDoc plugin setting file
227 | *.GhostDoc.xml
228 |
229 | # Node.js Tools for Visual Studio
230 | .ntvs_analysis.dat
231 |
232 | # Visual Studio 6 build log
233 | *.plg
234 |
235 | # Visual Studio 6 workspace options file
236 | *.opt
237 |
238 | # Visual Studio LightSwitch build output
239 | **/*.HTMLClient/GeneratedArtifacts
240 | **/*.DesktopClient/GeneratedArtifacts
241 | **/*.DesktopClient/ModelManifest.xml
242 | **/*.Server/GeneratedArtifacts
243 | **/*.Server/ModelManifest.xml
244 | _Pvt_Extensions
245 |
246 | # Paket dependency manager
247 | .paket/paket.exe
248 | paket-files/
249 |
250 | # FAKE - F# Make
251 | .fake/
252 |
253 | # JetBrains Rider
254 | .idea/
255 | *.sln.iml
256 | Artisan.Orm/1. How-to-publish-NuGet-package.txt
257 | desktop.ini
258 | Artisan.Orm/nuget.exe
259 |
--------------------------------------------------------------------------------
/Artisan.Orm/Artisan.Orm.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.1
5 | disable
6 | True
7 | Artisan.ORM
8 | 3.5.1
9 | Vadim Loboda
10 |
11 | ADO.NET Micro-ORM to SQL Server, implemented as .NET Standard 2.1 (since version 3.5.x) or a .Net Core 6.0 library (since version 3.0.0).
12 | Use version 2.x.x, which was built with Net Standard 2.0, if you want to utilise this library with the .Net Framework or a previous version of .NET Core.
13 |
14 | This library is designed to use stored procedures, table-valued parameters and structured static mappers, with the goal of reading and saving of complex object graphs at once in the fast, convinient and efficient way.
15 |
16 | Read more: https://www.codeproject.com/articles/1155836/artisan-orm-or-how-to-reinvent-the-wheel
17 |
18 | Copyright 2016-2025
19 | https://github.com/lobodava/artisan-orm
20 | Logo.png
21 | https://github.com/lobodava/artisan-orm
22 | Git
23 | ado.net orm micro-orm sql server mssql
24 | Added support for CancellationToken in asynchronous methods.
25 | Upgraded the Microsoft.Data.SqlClient package to version 5.2.3.
26 | LICENSE.txt
27 | Logo.ico
28 | Artisan.ORM
29 | Artisan.ORM
30 | README.md
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | True
41 | \
42 |
43 |
44 | True
45 | \
46 |
47 |
48 | True
49 | \
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/Artisan.Orm/DataReply.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.Serialization;
3 | using static System.String;
4 |
5 | namespace Artisan.Orm
6 | {
7 | [DataContract]
8 | public class DataReply {
9 |
10 | [DataMember]
11 | public DataReplyStatus Status { get; set; }
12 |
13 | [DataMember(EmitDefaultValue = false)]
14 | public DataReplyMessage[] Messages { get; set; }
15 |
16 | public DataReply()
17 | {
18 | Status = DataReplyStatus.Ok;
19 | Messages = null;
20 | }
21 |
22 | public DataReply(DataReplyStatus status)
23 | {
24 | Status = status;
25 | Messages = null;
26 | }
27 |
28 | public DataReply(DataReplyStatus status, string code, string text)
29 | {
30 | Status = status;
31 | Messages = new [] { new DataReplyMessage { Code = code, Text = text } };
32 | }
33 |
34 | public DataReply(DataReplyStatus status, DataReplyMessage message)
35 | {
36 | Status = status;
37 | if (message != null)
38 | Messages = new [] { message };
39 | }
40 |
41 | public DataReply(DataReplyStatus status, DataReplyMessage[] messages)
42 | {
43 | Status = status;
44 | if (messages?.Length > 0)
45 | Messages = messages;
46 | }
47 |
48 |
49 | public DataReply(string message)
50 | {
51 | Status = DataReplyStatus.Ok;
52 | Messages = new [] { new DataReplyMessage { Text = message } };
53 | }
54 |
55 | public static DataReplyStatus? ParseStatus (string statusCode)
56 | {
57 | if (IsNullOrWhiteSpace(statusCode))
58 | return null;
59 |
60 | if (Enum.TryParse(statusCode, true, out DataReplyStatus status))
61 | return status;
62 |
63 | throw new InvalidCastException(
64 | $"Cannot cast string '{statusCode}' to DataReplyStatus Enum. " +
65 | $"Available values: {Join(", ", Enum.GetNames(typeof(DataReplyStatus)))}");
66 | }
67 | }
68 |
69 |
70 | [DataContract]
71 | public class DataReply: DataReply {
72 |
73 | [DataMember(EmitDefaultValue = false)]
74 | public TData Data { get; set; }
75 |
76 | public DataReply(TData data)
77 | {
78 | Data = data;
79 | }
80 |
81 | public DataReply()
82 | {
83 | Data = default;
84 | }
85 |
86 | public DataReply(DataReplyStatus status, string code, string text, TData data) :base(status, code, text)
87 | {
88 | Data = data;
89 | }
90 |
91 | public DataReply(DataReplyStatus status, TData data) :base(status)
92 | {
93 | Data = data;
94 | }
95 |
96 | public DataReply(DataReplyStatus status) :base(status)
97 | {
98 | Data = default;
99 | }
100 |
101 | public DataReply(DataReplyStatus status, string code, string text) :base(status, code, text)
102 | {
103 | Data = default;
104 | }
105 |
106 | public DataReply(DataReplyStatus status, DataReplyMessage replyMessage) :base(status, replyMessage)
107 | {
108 | Data = default;
109 | }
110 |
111 | public DataReply(DataReplyStatus status, DataReplyMessage[] replyMessages) :base(status, replyMessages)
112 | {
113 | Data = default;
114 | }
115 | }
116 | }
117 |
118 |
--------------------------------------------------------------------------------
/Artisan.Orm/DataReplyException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Text;
4 |
5 | namespace Artisan.Orm
6 | {
7 |
8 | public class DataReplyException: Exception
9 | {
10 | public DataReplyStatus Status { get; } = DataReplyStatus.Error;
11 |
12 | public DataReplyMessage[] Messages { get; set; }
13 |
14 | public DataReplyException() {}
15 |
16 | public DataReplyException(DataReplyStatus status)
17 | {
18 | Status = status;
19 | }
20 |
21 | public DataReplyException(DataReplyStatus status, string code, long? id = null)
22 | {
23 | Status = status;
24 | Messages = new [] { new DataReplyMessage {Code = code, Id = id} };
25 | }
26 |
27 | public DataReplyException(DataReplyStatus status, string code, long? id, object value)
28 | {
29 | Status = status;
30 | Messages = new [] { new DataReplyMessage {Code = code, Id = id, Value = value} };
31 | }
32 |
33 | public DataReplyException(DataReplyStatus status, string code, string text, long? id = null)
34 | {
35 | Status = status;
36 | Messages = new [] { new DataReplyMessage {Code = code, Text = text, Id = id} };
37 | }
38 |
39 | public DataReplyException(DataReplyStatus status, string code, string text, object value)
40 | {
41 | Status = status;
42 | Messages = new [] { new DataReplyMessage {Code = code, Text = text, Value = value} };
43 | }
44 |
45 | public DataReplyException(DataReplyStatus status, string code, string text, long? id, object value)
46 | {
47 | Status = status;
48 | Messages = new [] { new DataReplyMessage {Code = code, Text = text, Id = id, Value = value} };
49 | }
50 |
51 | public DataReplyException(DataReplyStatus status, DataReplyMessage replyMessage)
52 | {
53 | Status = status;
54 | Messages = new [] { replyMessage };
55 | }
56 |
57 | public DataReplyException(DataReplyStatus status, DataReplyMessage[] messages)
58 | {
59 | Status = status;
60 | Messages = messages;
61 | }
62 |
63 | public override string Message
64 | {
65 | get
66 | {
67 | var sb = new StringBuilder();
68 |
69 | foreach (var message in Messages)
70 | {
71 | if (sb.Length > 0)
72 | sb.Append(", ");
73 |
74 | sb.Append('{').Append($"Code: {message.Code}");
75 |
76 | if (!String.IsNullOrWhiteSpace(message.Text))
77 | sb.Append($", Text: {message.Text}");
78 |
79 | if (message.Id.HasValue)
80 | sb.Append($", Id: {message.Id}");
81 |
82 | if (message.Value != null)
83 | sb.Append($", Value: {message.Value}");
84 |
85 | sb.Append('}');
86 | }
87 |
88 | return sb.ToString();
89 | }
90 | }
91 |
92 | public DataReplyMessage GetDataReplyMessage(string code)
93 | {
94 | return Messages?.FirstOrDefault(m => m.Code == code);
95 | }
96 |
97 | }
98 |
99 | }
100 |
--------------------------------------------------------------------------------
/Artisan.Orm/DataReplyMessage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.Serialization;
3 | using Microsoft.Data.SqlClient;
4 |
5 | namespace Artisan.Orm
6 | {
7 |
8 | [DataContract]
9 | public class DataReplyMessage
10 | {
11 | [DataMember]
12 | public String Code;
13 |
14 | [DataMember(EmitDefaultValue = false)]
15 | public String Text;
16 |
17 | [DataMember(EmitDefaultValue = false)]
18 | public Int64? Id;
19 |
20 | [DataMember(EmitDefaultValue = false)]
21 | public Object Value;
22 | }
23 |
24 | [MapperFor( typeof(DataReplyMessage), RequiredMethod.CreateObject)]
25 | public static class DataReplyMessageMapper
26 | {
27 | public static DataReplyMessage CreateObject(SqlDataReader dr)
28 | {
29 | var i = 0;
30 |
31 | return new DataReplyMessage
32 | {
33 | Code = dr.GetString (i) ,
34 | Text = dr.GetStringNullable (++i) ,
35 | Id = dr.GetValueNullable (++i) ,
36 | Value = dr.GetValueNullable