├── Framework.Net ├── FileDb.pfx ├── FileDb.sln ├── FileDb.snk └── Properties │ └── AssemblyInfo.cs ├── LICENSE ├── README.md ├── Src ├── AesEncryption.cs ├── DB.cs ├── DB2.cs ├── Exception.cs ├── FileDb.cs ├── FileDb.snk ├── FilterExpression.cs ├── HexEncoding.cs ├── RijndaelEncryption.cs ├── Table.cs └── enums.cs └── Standard.Net ├── FileDb.csproj ├── FileDb.sln ├── FileDb.snk └── Properties └── launchSettings.json /Framework.Net/FileDb.pfx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eztools-software/FileDb/3ab718a4eb43d7396b9c88608338a1aeb4d84fe6/Framework.Net/FileDb.pfx -------------------------------------------------------------------------------- /Framework.Net/FileDb.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.421 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileDb", "FileDb.csproj", "{7CFF3E3D-5717-45B6-ACCF-80BB1EAD9327}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Debug|Mixed Platforms = Debug|Mixed Platforms 12 | Debug|x86 = Debug|x86 13 | PCL|Any CPU = PCL|Any CPU 14 | PCL|Mixed Platforms = PCL|Mixed Platforms 15 | PCL|x86 = PCL|x86 16 | Release|Any CPU = Release|Any CPU 17 | Release|Mixed Platforms = Release|Mixed Platforms 18 | Release|x86 = Release|x86 19 | EndGlobalSection 20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 21 | {7CFF3E3D-5717-45B6-ACCF-80BB1EAD9327}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 22 | {7CFF3E3D-5717-45B6-ACCF-80BB1EAD9327}.Debug|Any CPU.Build.0 = Debug|Any CPU 23 | {7CFF3E3D-5717-45B6-ACCF-80BB1EAD9327}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 24 | {7CFF3E3D-5717-45B6-ACCF-80BB1EAD9327}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 25 | {7CFF3E3D-5717-45B6-ACCF-80BB1EAD9327}.Debug|x86.ActiveCfg = Debug|Any CPU 26 | {7CFF3E3D-5717-45B6-ACCF-80BB1EAD9327}.PCL|Any CPU.ActiveCfg = PCL|Any CPU 27 | {7CFF3E3D-5717-45B6-ACCF-80BB1EAD9327}.PCL|Any CPU.Build.0 = PCL|Any CPU 28 | {7CFF3E3D-5717-45B6-ACCF-80BB1EAD9327}.PCL|Mixed Platforms.ActiveCfg = PCL|Any CPU 29 | {7CFF3E3D-5717-45B6-ACCF-80BB1EAD9327}.PCL|Mixed Platforms.Build.0 = PCL|Any CPU 30 | {7CFF3E3D-5717-45B6-ACCF-80BB1EAD9327}.PCL|x86.ActiveCfg = PCL|x86 31 | {7CFF3E3D-5717-45B6-ACCF-80BB1EAD9327}.PCL|x86.Build.0 = PCL|x86 32 | {7CFF3E3D-5717-45B6-ACCF-80BB1EAD9327}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {7CFF3E3D-5717-45B6-ACCF-80BB1EAD9327}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {7CFF3E3D-5717-45B6-ACCF-80BB1EAD9327}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 35 | {7CFF3E3D-5717-45B6-ACCF-80BB1EAD9327}.Release|Mixed Platforms.Build.0 = Release|Any CPU 36 | {7CFF3E3D-5717-45B6-ACCF-80BB1EAD9327}.Release|x86.ActiveCfg = Release|Any CPU 37 | EndGlobalSection 38 | GlobalSection(SolutionProperties) = preSolution 39 | HideSolutionNode = FALSE 40 | EndGlobalSection 41 | GlobalSection(ExtensibilityGlobals) = postSolution 42 | SolutionGuid = {8E1F83D7-084F-4CC9-9163-A41E7324AD9B} 43 | EndGlobalSection 44 | EndGlobal 45 | -------------------------------------------------------------------------------- /Framework.Net/FileDb.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eztools-software/FileDb/3ab718a4eb43d7396b9c88608338a1aeb4d84fe6/Framework.Net/FileDb.snk -------------------------------------------------------------------------------- /Framework.Net/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( "FileDb Database - Windows version" )] 9 | [assembly: AssemblyDescription( "Local database for .NET" )] 10 | [assembly: AssemblyConfiguration( "" )] 11 | [assembly: AssemblyCompany( "EzTools Software (www.eztools-software.com)" )] 12 | [assembly: AssemblyProduct( "FileDb .NET Database Engine - Windows version" )] 13 | [assembly: AssemblyCopyright( "Copyright © EzTools Software - all rights reserved" )] 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( "a6596828-2937-43f7-bb14-7d02e189e13c" )] 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("7.4.3.0")] 36 | [assembly: AssemblyFileVersion("7.4.3.0")] 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FileDb 2 | A no-sql .NET database which implements a single table per file. 3 | 4 | FileDb is a free to use simple NoSQL database for .NET. The Standard.NET version allows cross-platform use across Windows, Xamarin Mac, Android and IOS. Use FileDb in your .NET and mobile phone applications where you need a simple, searchable, updatable local database. 5 | 6 | - Stores one table per file, including its index 7 | - Extremely small size DLL 8 | - Encryption fully supported - easily encrypt your data 9 | - Supports field types Int, UInt, Bool, String, Byte, Float, Double and DateTime and also arrays of the same types 10 | - Index supports a single Primary Key field (optional) 11 | - Ideal cross-platform database for mobile phone development using Xamarin 12 | - FileDb is VERY FAST 13 | - FileDb is FREE to use in your applications 14 | - Use with LINQ to Objects to achieve full relational capability 15 | - Supports typed datasets, so you can use either the built-in Table or your own POCO objects (Plain Old Class Object) 16 | - Database Explorer tool available at http://eztools-software.com/tools/FileDb/Explorer.asp 17 | -------------------------------------------------------------------------------- /Src/AesEncryption.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) EzTools Software - All Rights Reserved 2 | * Released under Mozilla Public License 2.0 3 | * Written and maintained by Brett Goodman 4 | */ 5 | using System.IO; 6 | using System.Text; 7 | #if !(NETFX_CORE || PCL) 8 | using System.Security.Cryptography; 9 | #endif 10 | 11 | using System.Reflection; 12 | 13 | namespace FileDbNs 14 | { 15 | public interface IEncryptor 16 | { 17 | byte[] Encrypt(byte[] dataToEncrypt); 18 | byte[] Decrypt(byte[] encryptedData); 19 | } 20 | 21 | /// 22 | /// Class uses the .NET AesManaged class for data encryption, but ONLY on the Windows platform 23 | /// build...the PCL build just returns the same data without doing anything. This is because 24 | /// encryption namespace isn't available for PCLs. In this case, create your own Encryptor 25 | /// class using the IEncryptor interface and set it into the FileDb object via SetEncryptor. 26 | /// 27 | /// 28 | public class AesEncryptor : IEncryptor 29 | { 30 | #if NETSTANDARD1_6 || NETFX_CORE || PCL 31 | public Encryptor( string hashKey, string productKey ) 32 | { 33 | } 34 | 35 | public byte[] Encrypt( byte[] dataToEncrypt ) 36 | { 37 | return dataToEncrypt; 38 | } 39 | 40 | public byte[] Decrypt( byte[] encryptedData ) 41 | { 42 | return encryptedData; 43 | } 44 | #else 45 | //byte[] _key; 46 | AesManaged _encryptor; 47 | 48 | /// 49 | /// Constructor taking a key (password) and salt as a string 50 | /// 51 | /// The password 52 | /// Salt 53 | /// 54 | public AesEncryptor(string encryptionKey, string salt) 55 | { 56 | init(encryptionKey, salt, null); 57 | } 58 | 59 | /// 60 | /// Constructor taking a key (password) and salt as a byte[] 61 | /// 62 | /// The password 63 | /// Salt 64 | /// 65 | public AesEncryptor(string encryptionKey, byte[] salt) 66 | { 67 | init(encryptionKey, salt, null); 68 | } 69 | 70 | /// 71 | /// Constructor taking a key (password) and salt as a string 72 | /// 73 | /// The password 74 | /// Salt 75 | /// Initialization Vector 76 | /// 77 | public AesEncryptor(string encryptionKey, string salt, byte[] iv) 78 | { 79 | init(encryptionKey, salt, iv); 80 | } 81 | 82 | /// 83 | /// Constructor taking a key (password) and salt as a byte[] 84 | /// 85 | /// The password 86 | /// Salt 87 | /// Initialization Vector 88 | /// 89 | public AesEncryptor(string encryptionKey, byte[] salt, byte[] iv) 90 | { 91 | init(encryptionKey, salt, iv); 92 | } 93 | 94 | void init(string encryptionKey, string salt, byte[] iv) 95 | { 96 | // Get the salt 97 | byte[] saltBytes; 98 | if (string.IsNullOrEmpty(salt)) 99 | saltBytes = GetDefaultSalt(encryptionKey); 100 | else 101 | saltBytes = Encoding.UTF8.GetBytes(salt); 102 | 103 | init(encryptionKey, saltBytes, iv); 104 | } 105 | 106 | void init(string encryptionKey, byte[] saltBytes, byte[] iv) 107 | { 108 | var key = GetHashKey(encryptionKey, saltBytes); 109 | 110 | createEncryptor(encryptionKey, key, iv); 111 | } 112 | 113 | void createEncryptor(string encryptionKey, byte[] key, byte[] iv) 114 | { 115 | _encryptor = new AesManaged(); 116 | 117 | // Set the key 118 | _encryptor.Key = key; 119 | if(iv != null) 120 | _encryptor.IV = iv; 121 | else 122 | _encryptor.IV = key; // must use the same IV 123 | } 124 | 125 | static byte[] GetDefaultSalt(string hashKey) 126 | { 127 | // gen for no salt 128 | var saltBytes = new byte[8]; 129 | for (int n = 0; n < 8; n++) 130 | saltBytes[n] = 0xff; 131 | return saltBytes; 132 | } 133 | 134 | static byte[] GetHashKey(string hashKey, byte[] saltBytes) 135 | { 136 | // Setup the hasher 137 | var rfc = new Rfc2898DeriveBytes(hashKey, saltBytes); 138 | // Return the key 139 | return rfc.GetBytes(16); 140 | } 141 | 142 | /// 143 | /// Encrypt the passed byte array 144 | /// 145 | /// The data to encrypt 146 | /// The encrypted data 147 | /// 148 | public byte[] Encrypt(byte[] dataToEncrypt) 149 | { 150 | byte[] bytes = null; 151 | MemoryStream outStrm = new MemoryStream((int) (dataToEncrypt.Length * 1.5)); 152 | 153 | // Create the crypto stream 154 | using (CryptoStream encrypt = new CryptoStream(outStrm, _encryptor.CreateEncryptor(), CryptoStreamMode.Write)) 155 | { 156 | // Encrypt 157 | encrypt.Write(dataToEncrypt, 0, dataToEncrypt.Length); 158 | encrypt.FlushFinalBlock(); 159 | bytes = outStrm.ToArray(); 160 | encrypt.Close(); 161 | } 162 | return bytes; 163 | } 164 | 165 | /// 166 | /// Decrypt the passed byte array 167 | /// 168 | /// The data to decrypt 169 | /// The decrypted data 170 | /// 171 | public byte[] Decrypt(byte[] encryptedData) 172 | { 173 | byte[] bytes = null; 174 | MemoryStream outStrm = new MemoryStream((int) (encryptedData.Length * 1.5)); 175 | 176 | // Create the crypto stream 177 | using (CryptoStream decrypt = new CryptoStream(outStrm, _encryptor.CreateDecryptor(), CryptoStreamMode.Write)) 178 | { 179 | // Encrypt 180 | decrypt.Write(encryptedData, 0, encryptedData.Length); 181 | decrypt.FlushFinalBlock(); 182 | bytes = outStrm.ToArray(); 183 | decrypt.Close(); 184 | } 185 | return bytes; 186 | } 187 | 188 | #endif 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /Src/DB2.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) EzTools Software - All Rights Reserved 2 | * Released under Mozilla Public License 2.0 3 | * Written and maintained by Brett Goodman 4 | */ 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Reflection; 9 | 10 | namespace FileDbNs 11 | { 12 | /* 13 | [System.AttributeUsage( System.AttributeTargets.Property )] 14 | public class FileDbField : System.Attribute 15 | { 16 | public FileDbField( string name ) 17 | { 18 | } 19 | }*/ 20 | 21 | public partial class FileDb 22 | { 23 | 24 | //---------------------------------------------------------------------------------------- 25 | // Create a table from the raw records 26 | // 27 | private List createTList(object[][] records, string[] fieldList, bool includeIndex, string[] orderByList) 28 | where T : class, new() 29 | { 30 | // get the field list 31 | int nExtra = includeIndex ? 1 : 0; 32 | Fields fields = null; 33 | if (fieldList != null) 34 | { 35 | fields = new Fields(fieldList.Length + nExtra); 36 | foreach (string fieldName in fieldList) 37 | { 38 | if (fields.ContainsKey(fieldName)) 39 | throw new FileDbException(string.Format(FileDbException.FieldSpecifiedTwice, fieldName), 40 | FileDbExceptionsEnum.FieldSpecifiedTwice); 41 | fields.Add(_dbEngine.Fields[fieldName]); 42 | } 43 | } 44 | else 45 | { 46 | fields = new Fields(_dbEngine.Fields.Count + nExtra); 47 | foreach (Field field in _dbEngine.Fields) 48 | { 49 | fields.Add(field); 50 | } 51 | } 52 | 53 | if (includeIndex) 54 | fields.Add(new Field(StrIndex, DataTypeEnum.Int32, fields.Count)); 55 | 56 | // use reflection to populate the Field properties 57 | 58 | #if NETSTANDARD1_6 || NETFX_CORE //|| PCL 59 | IEnumerable propertyInfos = typeof( T ).GetRuntimeProperties(); 60 | #else 61 | PropertyInfo[] propertyInfos = typeof(T).GetProperties(BindingFlags.Public | ~BindingFlags.Static); 62 | #endif 63 | 64 | Dictionary propsMap = new Dictionary(propertyInfos.Count()); 65 | 66 | foreach (Field field in fields) 67 | { 68 | PropertyInfo prop = propertyInfos.Where(p => string.Compare(p.Name, field.Name, StringComparison.CurrentCultureIgnoreCase) == 0).FirstOrDefault(); 69 | 70 | if (prop != null) 71 | { 72 | //Attribute attrib = Attribute.GetCustomAttribute( prop, typeof( FileDbField ) ); 73 | //if( attrib != null ) 74 | //{ 75 | // TODO: check that the datatypes match 76 | 77 | Type fieldType = null; 78 | 79 | switch (field.DataType) 80 | { 81 | case DataTypeEnum.Bool: 82 | fieldType = field.IsArray ? typeof(Boolean[]) : typeof(Boolean); 83 | break; 84 | case DataTypeEnum.Byte: 85 | fieldType = field.IsArray ? typeof(Byte[]) : typeof(Byte); 86 | break; 87 | case DataTypeEnum.Int32: 88 | fieldType = field.IsArray ? typeof(Int32[]) : typeof(Int32); 89 | break; 90 | case DataTypeEnum.UInt32: 91 | fieldType = field.IsArray ? typeof(UInt32[]) : typeof(UInt32); 92 | break; 93 | case DataTypeEnum.Int64: 94 | fieldType = field.IsArray ? typeof(Int64[]) : typeof(Int64); 95 | break; 96 | case DataTypeEnum.Single: 97 | fieldType = field.IsArray ? typeof(Single[]) : typeof(Single); 98 | break; 99 | case DataTypeEnum.Double: 100 | fieldType = field.IsArray ? typeof(Double[]) : typeof(Double); 101 | break; 102 | case DataTypeEnum.Decimal: 103 | fieldType = field.IsArray ? typeof(Decimal[]) : typeof(Decimal); 104 | break; 105 | case DataTypeEnum.DateTime: 106 | fieldType = field.IsArray ? typeof(DateTime[]) : typeof(DateTime); 107 | break; 108 | case DataTypeEnum.String: 109 | fieldType = field.IsArray ? typeof(String[]) : typeof(String); 110 | break; 111 | case DataTypeEnum.Guid: 112 | fieldType = field.IsArray ? typeof(Guid[]) : typeof(Guid); 113 | break; 114 | } 115 | 116 | // we use Contains rather than direct comparison because if the Property is Nullable type 117 | if (prop.PropertyType.FullName.Contains(fieldType.FullName) == false) 118 | { 119 | throw new Exception(string.Format("The type of Property {0} doesn't match the Field DataType - expected {1} but was {2}", 120 | prop.Name, fieldType, prop.PropertyType)); 121 | } 122 | 123 | propsMap.Add(field.Name, prop); 124 | //} 125 | } 126 | } 127 | 128 | List table = new List(records != null ? records.Length : 0); 129 | 130 | if (records != null) 131 | { 132 | foreach (object[] record in records) 133 | { 134 | T obj = new T(); 135 | 136 | for (int n = 0; n < fields.Count; n++) 137 | { 138 | Field field = fields[n]; 139 | 140 | if (propsMap.ContainsKey(field.Name)) 141 | { 142 | PropertyInfo prop = propsMap[field.Name]; 143 | 144 | if (prop.CanWrite) 145 | { 146 | object value = record[n]; 147 | prop.SetValue(obj, value, null); 148 | } 149 | } 150 | } 151 | 152 | table.Add(obj); 153 | } 154 | } 155 | return table; 156 | } 157 | 158 | #region SelecRecords 159 | 160 | #region SelectRecords FilterExpression 161 | 162 | //---------------------------------------------------------------------------------------- 163 | /// 164 | /// Return a List of custom objects filtered by the filter parameter. 165 | /// 166 | /// A FilterExpression representing the desired filter. 167 | /// A new List of custom objects with the requested Records 168 | /// 169 | public List SelectRecords(FilterExpression filter) 170 | where T : class, new() 171 | { 172 | return SelectRecords(filter, null, null, false); 173 | } 174 | 175 | //---------------------------------------------------------------------------------------- 176 | /// 177 | /// Return a List of custom objects filtered by the filter parameter. Only the specified Fields 178 | /// will be in the Table. 179 | /// 180 | /// A FilterExpression representing the desired filter. 181 | /// The desired fields to be in the returned Table 182 | /// A new List of custom objects with the requested Records and Fields 183 | /// 184 | public List SelectRecords(FilterExpression filter, string[] fieldList) 185 | where T : class, new() 186 | { 187 | return SelectRecords(filter, fieldList, null, false); 188 | } 189 | 190 | //---------------------------------------------------------------------------------------- 191 | /// 192 | /// Return a List of custom objects filtered by the filter parameter. Only the specified Fields 193 | /// will be in the Table. 194 | /// 195 | /// A FilterExpression representing the desired filter. 196 | /// The desired fields to be in the returned Table 197 | /// A list of one or more fields to order the returned table by, 198 | /// or null for default order. If an orderByField is prefixed with "!", that field will sorted 199 | /// in reverse order. 200 | /// A new List of custom objects with the requested Records and Fields ordered by the specified fields. 201 | /// 202 | public List SelectRecords(FilterExpression filter, string[] fieldList, string[] orderByList) 203 | where T : class, new() 204 | { 205 | return SelectRecords(filter, fieldList, orderByList, false); 206 | } 207 | 208 | //---------------------------------------------------------------------------------------- 209 | /// 210 | /// Get all records matching the search expression in the indicated order, if any. 211 | /// 212 | /// Represents a single search expression, such as ID = 3 213 | /// The list of fields to return or null for all fields 214 | /// If true, an additional Field named "index" will be returned 215 | /// which is the ordinal index of the Record in the database, which can be used in 216 | /// GetRecordByIndex and UpdateRecordByIndex. 217 | /// A list of one or more fields to order the returned table by, 218 | /// or null for default order. If an orderByField is prefixed with "!", that field will sorted 219 | /// in reverse order. 220 | /// A new List of custom objects with the requested Records and Fields 221 | /// 222 | public List SelectRecords(FilterExpression filter, string[] fieldList, string[] orderByList, bool includeIndex) 223 | where T : class, new() 224 | { 225 | lock (this) 226 | { 227 | object[][] records = _dbEngine.GetRecordByField(filter, fieldList, includeIndex, orderByList); 228 | return createTList(records, fieldList, includeIndex, orderByList); 229 | } 230 | } 231 | 232 | #endregion SelectRecords FilterExpression 233 | 234 | #region SelectRecords FilterExpressionGroup 235 | 236 | //---------------------------------------------------------------------------------------- 237 | /// 238 | /// Return a List of custom objects filtered by the filter parameter. 239 | /// 240 | /// A FilterExpressionGroup representing the desired filter. 241 | /// A new List of custom objects with the requested Records 242 | /// 243 | public List SelectRecords(FilterExpressionGroup filter) 244 | where T : class, new() 245 | { 246 | return SelectRecords(filter, null, null, false); 247 | } 248 | 249 | //---------------------------------------------------------------------------------------- 250 | /// 251 | /// Return a List of custom objects filtered by the filter parameter. Only the specified Fields 252 | /// will be in the Table. 253 | /// 254 | /// A FilterExpression representing the desired filter. 255 | /// The desired fields to be in the returned Table 256 | /// A new List of custom objects with the requested Records and Fields 257 | /// 258 | public List SelectRecords(FilterExpressionGroup filter, string[] fieldList) 259 | where T : class, new() 260 | { 261 | return SelectRecords(filter, fieldList, null, false); 262 | } 263 | 264 | //---------------------------------------------------------------------------------------- 265 | /// 266 | /// Return a List of custom objects filtered by the filter parameter. Only the specified Fields 267 | /// will be in the Table. 268 | /// 269 | /// A FilterExpression representing the desired filter. 270 | /// The desired fields to be in the returned Table 271 | /// A list of one or more fields to order the returned table by, 272 | /// or null for default order. If an orderByField is prefixed with "!", that field will sorted 273 | /// in reverse order. 274 | /// A new List of custom objects with the requested Records and Fields in the specified order 275 | /// 276 | public List SelectRecords(FilterExpressionGroup filter, string[] fieldList, string[] orderByList) 277 | where T : class, new() 278 | { 279 | return SelectRecords(filter, fieldList, orderByList, false); 280 | } 281 | 282 | //---------------------------------------------------------------------------------------- 283 | /// 284 | /// Get all records matching the FilterExpressionGroup in the indicated order, if any. 285 | /// 286 | /// Represents a compound search expression, such as FirstName = "John" AND LastName = "Smith" 287 | /// The list of fields to return or null for all fields 288 | /// Specify whether to include the record index as one of the Fields 289 | /// A list of one or more fields to order the returned table by, 290 | /// or null for default order. If an orderByField is prefixed with "!", that field will sorted 291 | /// in reverse order. 292 | /// A new List of custom objects with the requested Records and Fields 293 | /// 294 | public List SelectRecords(FilterExpressionGroup filter, string[] fieldList, string[] orderByList, bool includeIndex) 295 | where T : class, new() 296 | { 297 | lock (this) 298 | { 299 | object[][] records = _dbEngine.GetRecordByFields(filter, fieldList, includeIndex, orderByList); 300 | return createTList(records, fieldList, includeIndex, orderByList); 301 | } 302 | } 303 | 304 | #endregion SelectRecords FilterExpressionGroup 305 | 306 | #region SelectRecords string 307 | //---------------------------------------------------------------------------------------- 308 | /// 309 | /// Return a List of custom objects filtered by the filter parameter. 310 | /// 311 | /// A string representing the desired filter, eg. LastName = 'Fuller' 312 | /// A new List of custom objects with the requested Records 313 | /// 314 | public List SelectRecords(string filter) 315 | where T : class, new() 316 | { 317 | FilterExpressionGroup filterExpGrp = FilterExpressionGroup.Parse(filter); 318 | return SelectRecords(filterExpGrp); 319 | } 320 | 321 | //---------------------------------------------------------------------------------------- 322 | /// 323 | /// Return a List of custom objects filtered by the filter parameter. Only the specified Fields 324 | /// will be in the Table. 325 | /// 326 | /// A string representing the desired filter, eg. LastName = 'Fuller' 327 | /// The desired fields to be in the returned Table 328 | /// A new List of custom objects with the requested Records and Fields 329 | /// 330 | public List SelectRecords(string filter, string[] fieldList) 331 | where T : class, new() 332 | { 333 | FilterExpressionGroup filterExpGrp = FilterExpressionGroup.Parse(filter); 334 | return SelectRecords(filterExpGrp, fieldList); 335 | } 336 | 337 | //---------------------------------------------------------------------------------------- 338 | /// 339 | /// Return a List of custom objects filtered by the filter parameter. Only the specified Fields 340 | /// will be in the Table. 341 | /// 342 | /// A string representing the desired filter, eg. LastName = 'Fuller' 343 | /// The desired fields to be in the returned Table 344 | /// A list of one or more fields to order the returned table by, 345 | /// or null for default order. If an orderByField is prefixed with "!", that field will sorted 346 | /// in reverse order. 347 | /// A new List of custom objects with the requested Records and Fields ordered by the specified list 348 | /// 349 | public List SelectRecords(string filter, string[] fieldList, string[] orderByList) 350 | where T : class, new() 351 | { 352 | FilterExpressionGroup filterExpGrp = FilterExpressionGroup.Parse(filter); 353 | return SelectRecords(filterExpGrp, fieldList, orderByList); 354 | } 355 | 356 | //---------------------------------------------------------------------------------------- 357 | /// 358 | /// Return a List of custom objects filtered by the filter parameter. 359 | /// 360 | /// A string representing the desired filter, eg. LastName = 'Fuller' 361 | /// The desired fields to be in the returned Table 362 | /// If true, an additional Field named "index" will be returned 363 | /// which is the ordinal index of the Record in the database, which can be used in 364 | /// GetRecordByIndex and UpdateRecordByIndex 365 | /// A list of one or more fields to order the returned table by, 366 | /// or null for default order. If an orderByField is prefixed with "!", that field will sorted 367 | /// in reverse order 368 | /// A new List of custom objects with the requested Records and Fields 369 | /// 370 | public List SelectRecords(string filter, string[] fieldList, string[] orderByList, bool includeIndex) 371 | where T : class, new() 372 | { 373 | FilterExpressionGroup filterExpGrp = FilterExpressionGroup.Parse(filter); 374 | return SelectRecords(filterExpGrp, fieldList, orderByList, includeIndex); 375 | } 376 | #endregion SelectRecords string 377 | 378 | #region SelectAllRecords 379 | //---------------------------------------------------------------------------------------- 380 | /// 381 | /// Return all records in the database (table). 382 | /// 383 | /// A table containing all Records and Fields. 384 | /// 385 | public List SelectAllRecords() 386 | where T : class, new() 387 | { 388 | return SelectAllRecords(null, null, false); 389 | } 390 | 391 | //---------------------------------------------------------------------------------------- 392 | /// 393 | /// Return all records in the database (table). 394 | /// 395 | /// The list of Fields to return or null for all Fields 396 | /// A table containing all rows. 397 | /// 398 | public List SelectAllRecords(string[] fieldList) 399 | where T : class, new() 400 | { 401 | return SelectAllRecords(fieldList, null, false); 402 | } 403 | 404 | //---------------------------------------------------------------------------------------- 405 | /// 406 | /// Return all records in the database (table). 407 | /// 408 | /// The list of fields to return or null for all Fields 409 | /// A list of one or more fields to order the returned table by, 410 | /// or null for default order 411 | /// A table containing all rows. 412 | /// 413 | public List SelectAllRecords(string[] fieldList, string[] orderByList) 414 | where T : class, new() 415 | { 416 | return SelectAllRecords(fieldList, orderByList, false); 417 | } 418 | 419 | //---------------------------------------------------------------------------------------- 420 | /// 421 | /// Return all records in the database (table). 422 | /// 423 | /// Specify whether to include the Record index as one of the Fields 424 | /// A table containing all rows. 425 | /// 426 | public List SelectAllRecords(bool includeIndex) 427 | where T : class, new() 428 | { 429 | return SelectAllRecords(null, null, includeIndex); 430 | } 431 | 432 | //---------------------------------------------------------------------------------------- 433 | /// 434 | /// Return all records in the database (table). 435 | /// 436 | /// The list of fields to return or null for all fields 437 | /// Specify whether to include the record index as one of the Fields 438 | /// A list of one or more fields to order the returned table by, 439 | /// or null for default order 440 | /// A table containing all Records and the specified Fields. 441 | /// 442 | public List SelectAllRecords(string[] fieldList, string[] orderByList, bool includeIndex) 443 | where T : class, new() 444 | { 445 | lock (this) 446 | { 447 | object[][] records = _dbEngine.GetAllRecords(fieldList, includeIndex, orderByList); 448 | return createTList(records, fieldList, includeIndex, orderByList); 449 | } 450 | } 451 | #endregion SelectAllRecords 452 | 453 | #endregion SelecRecords 454 | 455 | #region GetRecord 456 | 457 | //---------------------------------------------------------------------------------------- 458 | /// 459 | /// Returns a single custom object at the current location. Meant to be used ONLY in conjunction 460 | /// with the MoveFirst/MoveNext methods. 461 | /// 462 | /// The list of fields to return or null for all fields 463 | /// Specify whether to include the record index as one of the Fields 464 | /// A new object or null 465 | /// 466 | public T GetCurrentRecord(string[] fieldList, bool includeIndex) 467 | where T : class, new() 468 | { 469 | lock (this) 470 | { 471 | object[] record = _dbEngine.GetCurrentRecord(includeIndex); 472 | return createT(record, fieldList, false); 473 | } 474 | } 475 | 476 | //---------------------------------------------------------------------------------------- 477 | /// 478 | /// 479 | /// Returns a single custom object specified by the index. 480 | /// 481 | /// The index of the record to return. This value can be obtained from 482 | /// Record returning queries by specifying true for the includeIndex parameter. 483 | /// The list of fields to return or null for all fields 484 | /// A new object or null 485 | /// 486 | public T GetRecordByIndex(Int32 index, string[] fieldList) 487 | where T : class, new() 488 | { 489 | lock (this) 490 | { 491 | object[] record = _dbEngine.GetRecordByIndex(index, fieldList, false); 492 | return createT(record, fieldList, false); 493 | } 494 | } 495 | 496 | //---------------------------------------------------------------------------------------- 497 | /// 498 | /// Returns a single custom object specified by the primary key value or record number. 499 | /// 500 | /// The primary key value. For databases without a primary key, 501 | /// 'key' is the zero-based record number in the table. 502 | /// The list of fields to return or null for all fields 503 | /// Specify whether to include the record index as one of the Fields 504 | /// A new object or null 505 | /// 506 | public T GetRecordByKey(object key, string[] fieldList, bool includeIndex) 507 | where T : class, new() 508 | { 509 | lock (this) 510 | { 511 | object[] record = _dbEngine.GetRecordByKey(key, fieldList, includeIndex); 512 | return createT(record, fieldList, includeIndex); 513 | } 514 | } 515 | 516 | T createT(object[] record, string[] fieldList, bool includeIndex) 517 | where T : class, new() 518 | { 519 | T obj = null; 520 | 521 | // TODO: try to make this more efficient by not creating a new Fields list 522 | 523 | if (record != null) 524 | { 525 | int nExtra = includeIndex ? 1 : 0; 526 | Fields fields = null; 527 | if (fieldList != null) 528 | { 529 | fields = new Fields(fieldList.Length + nExtra); 530 | foreach (string fieldName in fieldList) 531 | { 532 | if (fields.ContainsKey(fieldName)) 533 | throw new FileDbException(string.Format(FileDbException.FieldSpecifiedTwice, fieldName), FileDbExceptionsEnum.FieldSpecifiedTwice); 534 | fields.Add(_dbEngine.Fields[fieldName]); 535 | } 536 | } 537 | else 538 | { 539 | fields = new Fields(_dbEngine.Fields.Count + nExtra); 540 | foreach (Field field in _dbEngine.Fields) 541 | { 542 | fields.Add(field); 543 | } 544 | } 545 | 546 | if (includeIndex) 547 | fields.Add(new Field(StrIndex, DataTypeEnum.Int32, fields.Count)); 548 | 549 | obj = new T(); 550 | #if NETSTANDARD1_6 || NETFX_CORE //|| PCL 551 | IEnumerable propertyInfos = typeof( T ).GetRuntimeProperties(); 552 | #else 553 | PropertyInfo[] propertyInfos = typeof(T).GetProperties(BindingFlags.Public | ~BindingFlags.Static); 554 | #endif 555 | Dictionary propsMap = new Dictionary(propertyInfos.Count()); 556 | 557 | for (int n = 0; n < fields.Count; n++) 558 | { 559 | Field field = fields[n]; 560 | PropertyInfo prop = propertyInfos.Where(p => string.Compare(p.Name, field.Name, StringComparison.CurrentCultureIgnoreCase) == 0).FirstOrDefault(); 561 | 562 | if (prop != null) 563 | { 564 | if (prop.CanWrite) 565 | { 566 | object value = record[n]; 567 | prop.SetValue(obj, value, null); 568 | } 569 | } 570 | } 571 | } 572 | 573 | return obj; 574 | } 575 | 576 | #endregion GetRecord 577 | } 578 | } 579 | -------------------------------------------------------------------------------- /Src/Exception.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) EzTools Software - All Rights Reserved 2 | * Released under Mozilla Public License 2.0 3 | * Written and maintained by Brett Goodman 4 | */ 5 | using System; 6 | 7 | namespace FileDbNs 8 | { 9 | public enum FileDbExceptionsEnum 10 | { 11 | NoOpenDatabase, 12 | IndexOutOfRange, 13 | InvalidDatabaseSignature, 14 | CantOpenNewerDbVersion, 15 | DatabaseFileNotFound, 16 | InvalidTypeInSchema, 17 | InvalidPrimaryKeyType, 18 | MissingPrimaryKey, 19 | DuplicatePrimaryKey, 20 | DatabaseAlreadyHasPrimaryKey, 21 | PrimaryKeyCannotBeAdded, 22 | PrimaryKeyValueNotFound, 23 | InvalidFieldName, 24 | NeedIntegerKey, 25 | FieldNameAlreadyExists, 26 | NonArrayValue, 27 | InvalidDataType, 28 | MismatchedKeyFieldTypes, 29 | InvalidKeyFieldType, 30 | DatabaseEmpty, 31 | InvalidFilterConstruct, 32 | FieldSpecifiedTwice, 33 | IteratorPastEndOfFile, 34 | HashSetExpected, 35 | CantAddOrRemoveFieldWithDeletedRecords, 36 | DatabaseReadOnlyMode, 37 | InvalidMetaDataType, 38 | CantConvertTypeToGuid, 39 | GuidTypeMustBeGuidOrByteArray, 40 | ErrorConvertingValueForField, 41 | CannotDeletePrimaryKeyField, 42 | FieldListIsEmpty, 43 | FieldNameIsEmpty, 44 | InvalidOrderByFieldName, 45 | CannotOrderByOnArrayField, 46 | AsyncOperationTimeout, 47 | MissingTransactionFile, 48 | EmptyFilename, 49 | StreamMustBeWritable, 50 | NoCurrentTransaction, 51 | NoEncryptor, 52 | DbSchemaIsUpToDate, 53 | DbIsEncrypted, 54 | CryptographicException 55 | } 56 | 57 | // This exception is raised whenever a statement cannot be compiled. 58 | public class FileDbException : Exception 59 | { 60 | #region Strings 61 | internal const string IndexOutOfRange = "Index out of range"; 62 | 63 | internal const string RecordNumOutOfRange = "Record index out of range - {0}."; 64 | 65 | internal const string CantOpenNewerDbVersion = "Cannot open newer database version {0}.{1}. Current version is {2}"; 66 | 67 | internal const string StrInvalidDataType = "Invalid data type encountered in data file ({0})"; 68 | 69 | internal const string StrInvalidDataType2 = "Invalid data type for field '{0}' - expected '{1}' but got '{2}'"; 70 | 71 | internal const string InvalidFieldName = "Field name not in table: {0}"; 72 | 73 | internal const string InvalidKeyFieldType = "Invalid key field type (record number) - must be type Int32"; 74 | 75 | internal const string InvalidDateTimeType = "Invalid DateTime type"; 76 | 77 | internal const string DatabaseEmpty = "There are no records in the database"; 78 | 79 | internal const string NoOpenDatabase = "No open database"; 80 | 81 | internal const string DatabaseFileNotFound = "The database file doesn't exist"; 82 | 83 | internal const string NeedIntegerKey = "If there is no primary key on the database, the key must be the integer record number"; 84 | 85 | internal const string NonArrayValue = "Non array value passed for array field '{0}'"; 86 | 87 | internal const string InValidBoolType = "Invalid Bool type"; 88 | 89 | internal const string MismatchedKeyFieldTypes = "Mismatched key field types"; 90 | 91 | internal const string InvalidDatabaseSignature = "Invalid signature in database"; 92 | 93 | internal const string InvalidTypeInSchema = "Invalid type in schema: {0}"; 94 | 95 | internal const string InvalidPrimaryKeyType = "Primary key field '{0}' must be type Int or String and must not be Array type"; 96 | 97 | internal const string MissingPrimaryKey = "Primary key field {0} cannot be null or missing"; 98 | 99 | internal const string DuplicatePrimaryKey = "Duplicate key violation - Field: '{0}' - Value: '{1}'"; 100 | 101 | internal const string PrimaryKeyValueNotFound = "Primary key field value not found"; 102 | 103 | internal const string InvalidFilterConstruct = "Invalid Filter construct near '{0}'"; 104 | 105 | internal const string FieldSpecifiedTwice = "Field name cannot be specified twice in list - {0}"; 106 | 107 | internal const string IteratorPastEndOfFile = "The current position is past the last record - call MoveFirst to reset the current position"; 108 | 109 | internal const string HashSetExpected = "HashSet expected as the SearchVal when using Equality.In"; 110 | 111 | internal const string CantAddOrRemoveFieldWithDeletedRecords = "Cannot add or remove fields with deleted records in the database - call Clean first"; 112 | 113 | internal const string CannotDeletePrimaryKeyField = "You cannot delete the primary key field ({0})"; 114 | 115 | internal const string FieldListIsEmpty = "The field list is null or empty"; 116 | 117 | internal const string DatabaseAlreadyHasPrimaryKey = "This database already has a primary key field ({0})"; 118 | 119 | internal const string PrimaryKeyCannotBeAdded = "Primary key fields can only be added if there are no records in the database: {0}"; 120 | 121 | internal const string FieldNameAlreadyExists = "Cannot add field because the field name already exists: {0}"; 122 | 123 | internal const string DatabaseReadOnlyMode = "Database is open in read-only mode"; 124 | 125 | internal const string InvalidMetaDataType = "Invalid meta data type - must be String or Byte[]"; 126 | 127 | internal const string CantConvertTypeToGuid = "Cannot convert type {0} to Guid"; 128 | 129 | internal const string GuidTypeMustBeGuidOrByteArray = "Guid type must be Guid or Byte array"; 130 | 131 | internal const string ErrorConvertingValueForField = "Error converting value for field name: {0} value: {1}"; 132 | 133 | internal const string FieldNameIsEmpty = "The field name is null or empty"; 134 | 135 | internal const string InvalidOrderByFieldName = "Invalid OrderBy field name - {0}"; 136 | 137 | internal const string CannotOrderByOnArrayField = "Cannot OrderBy on an array field"; 138 | 139 | internal const string AsyncOperationTimeout = "Async operation timeout"; 140 | 141 | internal const string MissingTransactionFile = "Missing transaction file"; 142 | 143 | internal const string EmptyFilename = "The database filename cannot be null or empty"; 144 | 145 | internal const string StreamMustBeWritable = "The Stream must be writable to create a database"; 146 | 147 | internal const string NoCurrentTransaction = "There is no current transaction"; 148 | 149 | internal const string NoEncryptor = "An Encryptor was not provided when the DB was opened"; 150 | 151 | internal const string DbSchemaIsUpToDate = "The database schema is already up to date"; 152 | 153 | internal const string DbIsEncrypted = "This database is encrypted but no Encryptor was provided"; 154 | 155 | internal const string CryptographicException = "Cryptographic exception - likely cause is bad password"; 156 | 157 | #endregion Strings 158 | 159 | ///////////////////////////////////////// 160 | FileDbExceptionsEnum _id; 161 | 162 | public FileDbExceptionsEnum ID 163 | { 164 | get { return _id; } 165 | } 166 | 167 | public FileDbException(string message, FileDbExceptionsEnum id) 168 | : base(message) 169 | { 170 | _id = id; 171 | } 172 | 173 | public FileDbException(string message, FileDbExceptionsEnum id, Exception cause) 174 | : base(message, cause) 175 | { 176 | _id = id; 177 | } 178 | } 179 | } 180 | 181 | -------------------------------------------------------------------------------- /Src/FileDb.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eztools-software/FileDb/3ab718a4eb43d7396b9c88608338a1aeb4d84fe6/Src/FileDb.snk -------------------------------------------------------------------------------- /Src/FilterExpression.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) EzTools Software - All Rights Reserved 2 | * Released under Mozilla Public License 2.0 3 | * Written and maintained by Brett Goodman 4 | */ 5 | using System; 6 | using System.Text; 7 | using System.Diagnostics; 8 | using System.Collections.Generic; 9 | using System.Reflection; 10 | using System.Text.RegularExpressions; 11 | 12 | namespace FileDbNs 13 | { 14 | 15 | //===================================================================== 16 | /// 17 | /// Use this class for single field searches. 18 | /// 19 | /// 20 | public class FilterExpression 21 | { 22 | /// 23 | /// Create a FilterExpression with the indicated values 24 | /// 25 | /// The name of the Field to filter on 26 | /// The Field value to filter on 27 | /// The Equality operator to use in the value comparison 28 | /// 29 | public FilterExpression(string fieldName, object searchVal, ComparisonOperatorEnum equality) 30 | : this(fieldName, searchVal, equality, MatchTypeEnum.UseCase, false) 31 | { 32 | } 33 | 34 | /// 35 | /// Create a FilterExpression with the indicated values 36 | /// 37 | /// The name of the Field to filter on 38 | /// The Field value to filter on 39 | /// The Equality operator to use in the value comparison 40 | /// The match type, eg. MatchType.Exact 41 | /// 42 | public FilterExpression(string fieldName, object searchVal, ComparisonOperatorEnum equality, MatchTypeEnum matchType) 43 | { 44 | FieldName = fieldName; 45 | SearchVal = searchVal; 46 | MatchType = matchType; 47 | Equality = equality; 48 | IsNot = false; 49 | } 50 | 51 | /// 52 | /// Create a FilterExpression with the indicated values 53 | /// 54 | /// The name of the Field to filter on 55 | /// The Field value to filter on 56 | /// The Equality operator to use in the value comparison 57 | /// The match type, eg. MatchType.Exact 58 | /// Operator negation 59 | /// 60 | public FilterExpression(string fieldName, object searchVal, ComparisonOperatorEnum equality, MatchTypeEnum matchType, bool isNot) 61 | { 62 | FieldName = fieldName; 63 | SearchVal = searchVal; 64 | MatchType = matchType; 65 | Equality = equality; 66 | IsNot = isNot; 67 | } 68 | 69 | internal BoolOpEnum BoolOp { get; set; } 70 | 71 | public string FieldName { get; set; } 72 | public object SearchVal { get; set; } 73 | public MatchTypeEnum MatchType { get; set; } 74 | public ComparisonOperatorEnum Equality { get; set; } 75 | public bool IsNot { get; set; } 76 | 77 | /// 78 | /// Parse the expression string to create a FilterExpressionGroup representing a simple expression. 79 | /// 80 | /// The string expression. Example: LastName = 'Fuller' 81 | /// A new FilterExpression representing the simple expression 82 | /// 83 | public static FilterExpression Parse(string expression) 84 | { 85 | FilterExpression fexp = null; 86 | FilterExpressionGroup fexpg = FilterExpressionGroup.Parse(expression); 87 | if (fexpg != null) 88 | fexp = fexpg.Expressions[0] as FilterExpression; 89 | 90 | return fexp; 91 | } 92 | 93 | /// 94 | /// Utility method to transform a filesystem wildcard pattern into a regex pattern 95 | /// eg. december* or mary? 96 | /// 97 | /// 98 | /// 99 | /// 100 | public static string WildcardToRegex(string pattern) 101 | { 102 | string result = Regex.Escape(pattern) 103 | .Replace(@"\*", ".+?") 104 | .Replace(@"\?", "."); 105 | 106 | if (result.EndsWith(".+?")) 107 | { 108 | result = result.Remove(result.Length - 3, 3); 109 | result += ".*"; 110 | } 111 | 112 | return result; 113 | } 114 | 115 | /// 116 | /// Create a FilterExpression of type "IN". This type is a HashSet of the values which will be used 117 | /// to filter the query. 118 | /// 119 | /// The name of the Field which will be used in the FilterExpressions 120 | /// A Table to use to build the IN FilterExpressions 121 | /// The name of the Field in the Table which holds the value to be used to build the IN FilterExpressions 122 | /// A new FilterExpression 123 | /// 124 | public static FilterExpression CreateInExpressionFromTable(string fieldName, Table table, string fieldNameInTable) 125 | { 126 | if (!table.Fields.ContainsKey(fieldNameInTable)) 127 | throw new Exception(string.Format("Field {0} is not in the table", fieldNameInTable)); 128 | 129 | FilterExpression fexp = null; 130 | 131 | var hashSet = new HashSet(); 132 | 133 | foreach (Record record in table) 134 | { 135 | object val = record[fieldNameInTable]; 136 | hashSet.Add(val); 137 | } 138 | 139 | fexp = new FilterExpression(fieldName, hashSet, ComparisonOperatorEnum.In); 140 | 141 | return fexp; 142 | } 143 | 144 | /// 145 | /// Create a FilterExpression of type "IN". This type is a HashSet of the values which will be used 146 | /// to filter the query. 147 | /// 148 | /// The name of the Field which will be used in the FilterExpressions 149 | /// A List of custom objects to use to build the IN FilterExpression 150 | /// The name of the Property of the custom class which holds the value to be 151 | /// used to build the IN FilterExpression 152 | /// A new FilterExpression 153 | /// 154 | public static FilterExpression CreateInExpressionFromList(string fieldName, IList list, string propertyName) 155 | { 156 | FilterExpression fexp = null; 157 | 158 | var hashSet = new HashSet(); 159 | 160 | Type type = typeof(T); 161 | #if NETSTANDARD1_6 || NETFX_CORE //|| PCL 162 | PropertyInfo prop = type.GetRuntimeProperties().FirstOrDefault( p => p.PropertyType == type ); 163 | #else 164 | PropertyInfo prop = type.GetProperty(propertyName); 165 | #endif 166 | 167 | if (prop == null) 168 | throw new Exception(string.Format("Field {0} is not a property of {1}", propertyName, type.Name)); 169 | 170 | foreach (T obj in list) 171 | { 172 | object val = prop.GetValue(obj, null); 173 | hashSet.Add(val); 174 | } 175 | 176 | fexp = new FilterExpression(fieldName, hashSet, ComparisonOperatorEnum.In); 177 | 178 | return fexp; 179 | } 180 | 181 | /// 182 | /// Create a FilterExpression of type "IN". This type is a HashSet of the values which will be used 183 | /// to filter the query. 184 | /// 185 | /// The name of the Field which will be used in the FilterExpressions 186 | /// A List of strings to use to build the IN FilterExpression 187 | /// A new FilterExpression 188 | /// 189 | public static FilterExpression CreateInExpressionFromList(string fieldName, IList list) 190 | { 191 | FilterExpression fexp = null; 192 | 193 | var hashSet = new HashSet(); 194 | 195 | foreach (string s in list) 196 | { 197 | hashSet.Add(s); 198 | } 199 | 200 | fexp = new FilterExpression(fieldName, hashSet, ComparisonOperatorEnum.In); 201 | 202 | return fexp; 203 | } 204 | 205 | /// 206 | /// Create a FilterExpression of type "IN". This type is a HashSet of the values which will be used 207 | /// to filter the query. 208 | /// 209 | /// The name of the Field which will be used in the FilterExpressions 210 | /// A List of strings to use to build the IN FilterExpression 211 | /// A new FilterExpression 212 | /// 213 | public static FilterExpression CreateInExpressionFromList(string fieldName, IList list) 214 | { 215 | FilterExpression fexp = null; 216 | 217 | var hashSet = new HashSet(); 218 | 219 | foreach (Int32 id in list) 220 | { 221 | hashSet.Add(id); 222 | } 223 | 224 | fexp = new FilterExpression(fieldName, hashSet, ComparisonOperatorEnum.In); 225 | 226 | return fexp; 227 | } 228 | } 229 | 230 | //===================================================================== 231 | /// 232 | /// Use this class to group FilterExpression and FilterExpressionGroup to form compound search expressions. 233 | /// All expressions in the group will be evaluated by the same boolean And/Or operation. Use multiple 234 | /// FilterExpressionGroups to form any combination of And/Or logic. 235 | /// 236 | /// 237 | public class FilterExpressionGroup 238 | { 239 | List _expressions = new List(); 240 | 241 | public FilterExpressionGroup() 242 | { 243 | } 244 | 245 | internal BoolOpEnum BoolOp { get; set; } 246 | 247 | public void Add(BoolOpEnum boolOp, object searchExpressionOrGroup) 248 | { 249 | // set the BoolOp into the expression or group 250 | if (searchExpressionOrGroup is FilterExpression) 251 | ((FilterExpression) searchExpressionOrGroup).BoolOp = boolOp; 252 | else if (searchExpressionOrGroup is FilterExpressionGroup) 253 | ((FilterExpressionGroup) searchExpressionOrGroup).BoolOp = boolOp; 254 | 255 | _expressions.Add(searchExpressionOrGroup); 256 | } 257 | 258 | public List Expressions 259 | { 260 | get { return _expressions; } 261 | } 262 | 263 | /// 264 | /// Parse the expression string to create a FilterExpressionGroup representing a compound expression. 265 | /// 266 | /// The string compound expression. Example: (FirstName ~= 'andrew' OR FirstName ~= 'nancy') AND LastName = 'Fuller' 267 | /// A new FilterExpressionGroup representing the compound expression 268 | /// 269 | public static FilterExpressionGroup Parse(string expression) 270 | { 271 | FilterExpressionGroup srchExpGrp = null; 272 | 273 | if (expression == null) 274 | return srchExpGrp; 275 | 276 | srchExpGrp = new FilterExpressionGroup(); 277 | 278 | int n = 0; 279 | parseExpression(expression, ref n, srchExpGrp); 280 | 281 | while (true) 282 | { 283 | if (srchExpGrp.Expressions.Count != 1) 284 | break; 285 | 286 | // if there is only one Group in a Group, then return that one 287 | 288 | FilterExpressionGroup grp = srchExpGrp.Expressions[0] as FilterExpressionGroup; 289 | if (grp == null) 290 | break; // its an Expression, NOT a group 291 | 292 | // remove this group from its parent 293 | srchExpGrp.Expressions.Clear(); 294 | srchExpGrp = grp; 295 | } 296 | 297 | return srchExpGrp; 298 | } 299 | 300 | internal enum ParseState { Left, Right, CompareOp, BoolOp } 301 | 302 | static void parseExpression(string filter, ref int pos, FilterExpressionGroup parentSrchExpGrp) 303 | { 304 | ParseState state = ParseState.Left; 305 | bool hasBracket = false, 306 | inString = false, 307 | isNot = false; 308 | string fieldName = null; 309 | object searchVal = null; 310 | var sbTemp = new StringBuilder(); 311 | ComparisonOperatorEnum comparisonOp = ComparisonOperatorEnum.Equal; 312 | MatchTypeEnum matchType = MatchTypeEnum.UseCase; 313 | BoolOpEnum curBoolOp = BoolOpEnum.And; 314 | int startPos = pos; 315 | 316 | 317 | // skip past any leading spaces 318 | while (pos < filter.Length && char.IsWhiteSpace(filter[pos])) pos++; 319 | 320 | for (; pos < filter.Length;) 321 | { 322 | ////////////////////////////////////////////////////////// 323 | #region Left 324 | if (state == ParseState.Left) 325 | { 326 | if (filter[pos] == '[') // field names with ' ' in them must be wrapped with brackets 327 | { 328 | hasBracket = true; 329 | pos++; 330 | startPos = pos; 331 | } 332 | 333 | if (hasBracket) 334 | { 335 | // look for ending bracket 336 | if (filter[pos] == ']') 337 | { 338 | fieldName = filter.Substring(startPos, pos - startPos).Trim(); 339 | pos++; // skip past bracket 340 | } 341 | } 342 | else // no bracket - look for non-alpha 343 | { 344 | if (filter[pos] == '(') 345 | { 346 | // start of a new FilterExpressionGroup 347 | pos++; 348 | var newSrchExpGrp = new FilterExpressionGroup(); 349 | parentSrchExpGrp.Add(curBoolOp, newSrchExpGrp); 350 | parseExpression(filter, ref pos, newSrchExpGrp); 351 | state = ParseState.BoolOp; 352 | } 353 | else if (filter[pos] == '~') // eg. ~LastName 354 | { 355 | matchType = MatchTypeEnum.IgnoreCase; 356 | } 357 | else if (char.IsWhiteSpace(filter[pos]) || 358 | (!char.IsLetterOrDigit(filter[pos]) && filter[pos] != '_' && filter[pos] != '~')) 359 | // field names with spaces in them must be wrapped with brackets 360 | { 361 | fieldName = filter.Substring(startPos, pos - startPos).Trim(); 362 | } 363 | } 364 | 365 | if (fieldName != null) 366 | { 367 | if (fieldName[0] == '~') 368 | { 369 | fieldName = fieldName.Substring(1); 370 | matchType = MatchTypeEnum.IgnoreCase; 371 | } 372 | state = ParseState.CompareOp; 373 | } 374 | else 375 | { 376 | pos++; 377 | } 378 | } 379 | #endregion Left 380 | ////////////////////////////////////////////////////////// 381 | #region CompareOp 382 | else if (state == ParseState.CompareOp) 383 | { 384 | // skip whitespace 385 | while (pos < filter.Length && char.IsWhiteSpace(filter[pos])) pos++; 386 | 387 | if (char.IsLetter(filter[pos])) // REGEX 388 | { 389 | // should be CONTAINS, REGEX, IN or NOT 390 | //if( pos + 4 >= filter.Length ) 391 | // throwInvalidFilterConstruct( filter, pos ); 392 | 393 | try 394 | { 395 | // NOT 396 | if (char.ToUpper(filter[pos]) == 'N' && char.ToUpper(filter[pos + 1]) == 'O' && 397 | char.ToUpper(filter[pos + 2]) == 'T' && char.IsWhiteSpace(filter[pos + 3])) 398 | { 399 | pos += 3; 400 | isNot = true; 401 | continue; 402 | } 403 | // IN 404 | else if (char.ToUpper(filter[pos]) == 'I' && char.ToUpper(filter[pos + 1]) == 'N' && 405 | (char.IsWhiteSpace(filter[pos + 2]) || filter[pos + 2] == '(')) 406 | { 407 | pos += 2; 408 | if (char.IsWhiteSpace(filter[pos])) // skip whitespace 409 | { 410 | while (pos < filter.Length && char.IsWhiteSpace(filter[pos])) pos++; 411 | if (filter[pos] != '(') 412 | throwInvalidFilterConstruct(filter, pos - 2); 413 | } 414 | comparisonOp = ComparisonOperatorEnum.In; 415 | } 416 | // REGEX 417 | else if (char.ToUpper(filter[pos]) == 'R' && char.ToUpper(filter[pos + 1]) == 'E' && 418 | char.ToUpper(filter[pos + 2]) == 'G' && char.ToUpper(filter[pos + 3]) == 'E' && 419 | char.ToUpper(filter[pos + 4]) == 'X' && char.IsWhiteSpace(filter[pos + 5])) 420 | { 421 | pos += 5; 422 | comparisonOp = ComparisonOperatorEnum.Regex; 423 | } 424 | // CONTAINS 425 | else if (char.ToUpper(filter[pos]) == 'C' && char.ToUpper(filter[pos + 1]) == 'O' && 426 | char.ToUpper(filter[pos + 2]) == 'N' && char.ToUpper(filter[pos + 3]) == 'T' && 427 | char.ToUpper(filter[pos + 4]) == 'A' && char.ToUpper(filter[pos + 5]) == 'I' && 428 | char.ToUpper(filter[pos + 6]) == 'N' && char.ToUpper(filter[pos + 7]) == 'S' && 429 | char.IsWhiteSpace(filter[pos + 8])) 430 | { 431 | pos += 8; 432 | comparisonOp = ComparisonOperatorEnum.Contains; 433 | } 434 | else 435 | throwInvalidFilterConstruct(filter, pos - 2); 436 | 437 | } 438 | catch //( Exception ex ) 439 | { 440 | throwInvalidFilterConstruct(filter, pos - 2); 441 | } 442 | } 443 | // alternative way to specify ignore case search (other way is to prefix a fieldname with ~) 444 | else if (filter[pos] == '~') // ~= 445 | { 446 | matchType = MatchTypeEnum.IgnoreCase; 447 | if (++pos >= filter.Length) 448 | throwInvalidFilterConstruct(filter, pos); 449 | 450 | // next char must be = 451 | if (filter[pos] != '=') 452 | throwInvalidFilterConstruct(filter, pos); 453 | 454 | comparisonOp = ComparisonOperatorEnum.Equal; 455 | } 456 | else if (filter[pos] == '!') // != 457 | { 458 | if (++pos >= filter.Length) 459 | throwInvalidFilterConstruct(filter, pos); 460 | 461 | // next char must be = 462 | if (filter[pos] != '=') 463 | throwInvalidFilterConstruct(filter, pos); 464 | 465 | comparisonOp = ComparisonOperatorEnum.Equal; 466 | isNot = true; 467 | } 468 | else if (filter[pos] == '=') 469 | { 470 | comparisonOp = ComparisonOperatorEnum.Equal; 471 | } 472 | else if (filter[pos] == '<') // <, <= or <> 473 | { 474 | if (pos + 1 >= filter.Length) 475 | throwInvalidFilterConstruct(filter, pos); 476 | 477 | if (filter[pos + 1] == '>') 478 | { 479 | pos++; 480 | comparisonOp = ComparisonOperatorEnum.Equal; 481 | isNot = true; 482 | } 483 | else if (filter[pos + 1] == '=') 484 | { 485 | pos++; 486 | comparisonOp = ComparisonOperatorEnum.LessThanOrEqual; 487 | } 488 | else 489 | comparisonOp = ComparisonOperatorEnum.LessThan; 490 | } 491 | else if (filter[pos] == '>') // > or >= 492 | { 493 | if (pos + 1 >= filter.Length) 494 | throwInvalidFilterConstruct(filter, pos); 495 | 496 | if (filter[pos + 1] == '=') 497 | { 498 | pos++; 499 | comparisonOp = ComparisonOperatorEnum.GreaterThanOrEqual; 500 | } 501 | else 502 | comparisonOp = ComparisonOperatorEnum.GreaterThan; 503 | } 504 | else 505 | { 506 | throwInvalidFilterConstruct(filter, pos); 507 | } 508 | pos++; 509 | state = ParseState.Right; 510 | } 511 | #endregion CompareOp 512 | ////////////////////////////////////////////////////////// 513 | #region Right 514 | else if (state == ParseState.Right) 515 | { 516 | if (comparisonOp == ComparisonOperatorEnum.In) //|| comparisonOp == EqualityEnum.NotIn ) 517 | { 518 | // skip whitespace 519 | while (pos < filter.Length && char.IsWhiteSpace(filter[pos])) pos++; 520 | 521 | // filter[pos] should look like this now: (val1, val2, val3) 522 | // or like this: ('val1', 'val2', 'val3') 523 | 524 | if (filter[pos] == '(') 525 | pos++; 526 | 527 | // find the end 528 | int endPos = pos; 529 | 530 | while (endPos < filter.Length && filter[endPos] != ')') 531 | { 532 | endPos++; 533 | } 534 | if (endPos >= filter.Length) 535 | throw new FileDbException(string.Format(FileDbException.InvalidFilterConstruct, filter.Substring(pos)), 536 | FileDbExceptionsEnum.InvalidFilterConstruct); 537 | 538 | string inVals = filter.Substring(pos, endPos - pos); 539 | 540 | searchVal = parseInVals(inVals, matchType); 541 | 542 | pos = endPos; 543 | } 544 | else 545 | { 546 | if (!inString) 547 | { 548 | // skip whitespace only if we haven't found anything yet 549 | if (sbTemp.Length == 0) 550 | while (pos < filter.Length && char.IsWhiteSpace(filter[pos])) pos++; 551 | 552 | // look for end of ExpressionGroup 553 | if (sbTemp.Length > 0 && (filter[pos] == ')' || char.IsWhiteSpace(filter[pos]))) 554 | { 555 | // Expression completed 556 | searchVal = sbTemp.ToString(); 557 | sbTemp.Length = 0; 558 | // BG: Fix added 24-6-19 559 | if (string.Compare((string) searchVal, "null", StringComparison.OrdinalIgnoreCase) == 0) 560 | searchVal = null; 561 | var srchExp = new FilterExpression(fieldName, searchVal, comparisonOp, matchType, isNot); 562 | parentSrchExpGrp.Add(curBoolOp, srchExp); 563 | if (filter[pos] == ')') 564 | return; 565 | fieldName = null; 566 | state = ParseState.BoolOp; 567 | } 568 | else if (sbTemp.Length == 0 && filter[pos] == '\'') 569 | { 570 | // just starting to get the value 571 | inString = /*isString=*/ true; 572 | } 573 | else 574 | sbTemp.Append(filter[pos]); 575 | } 576 | else // inString == true 577 | { 578 | if (filter[pos] == '\'') 579 | { 580 | //Debug.Assert( sbTemp.Length > 0 ); -- it could be empty, eg. myfield = '' 581 | 582 | // if the next char is NOT another ' (escaped) then the string is completed 583 | if ((pos + 1 < filter.Length) && filter[pos + 1] == '\'') 584 | pos++; 585 | else 586 | { 587 | inString = false; 588 | searchVal = sbTemp.ToString(); 589 | sbTemp.Length = 0; 590 | var srchExp = new FilterExpression(fieldName, searchVal, comparisonOp, matchType, isNot); 591 | parentSrchExpGrp.Add(curBoolOp, srchExp); 592 | fieldName = null; 593 | state = ParseState.BoolOp; 594 | goto Advance; 595 | } 596 | } 597 | sbTemp.Append(filter[pos]); 598 | } 599 | } 600 | Advance: 601 | // advance 602 | pos++; 603 | } 604 | #endregion Right 605 | ////////////////////////////////////////////////////////// 606 | #region Next 607 | else // if( state == ParseState.BoolOp ) 608 | { 609 | Debug.Assert(state == ParseState.BoolOp); 610 | 611 | if (sbTemp.Length == 0) 612 | // skip whitespace 613 | while (pos < filter.Length && char.IsWhiteSpace(filter[pos])) pos++; 614 | 615 | if (filter[pos] == ')') 616 | return; // we must be finished 617 | 618 | if (char.IsWhiteSpace(filter[pos])) 619 | { 620 | // we must be finished 621 | if (sbTemp.Length == 0) 622 | throw new FileDbException(string.Format(FileDbException.InvalidFilterConstruct, filter.Substring(pos)), 623 | FileDbExceptionsEnum.InvalidFilterConstruct); 624 | 625 | string sOp = sbTemp.ToString(); 626 | sbTemp.Length = 0; 627 | 628 | if (string.Compare(sOp, "AND", StringComparison.OrdinalIgnoreCase) == 0) 629 | { 630 | curBoolOp = BoolOpEnum.And; 631 | } 632 | else if (string.Compare(sOp, "OR", StringComparison.OrdinalIgnoreCase) == 0) 633 | { 634 | curBoolOp = BoolOpEnum.Or; 635 | } 636 | else 637 | { 638 | throw new FileDbException(string.Format(FileDbException.InvalidFilterConstruct, filter.Substring(pos)), 639 | FileDbExceptionsEnum.InvalidFilterConstruct); 640 | } 641 | 642 | state = ParseState.Left; // start over on next expression 643 | 644 | // skip whitespace 645 | while (pos < filter.Length && char.IsWhiteSpace(filter[pos])) pos++; 646 | 647 | // reset vars 648 | startPos = pos; 649 | hasBracket = false; 650 | } 651 | else 652 | { 653 | sbTemp.Append(filter[pos]); 654 | pos++; 655 | } 656 | } 657 | #endregion Next 658 | 659 | } // for... 660 | 661 | // did we just complete an Expression? 662 | if (state == ParseState.Right) 663 | { 664 | if (comparisonOp != ComparisonOperatorEnum.In) //&& comparisonOp != EqualityEnum.NotIn ) 665 | { 666 | searchVal = sbTemp.ToString(); 667 | if (!inString && string.Compare((string) searchVal, "null", StringComparison.OrdinalIgnoreCase) == 0) 668 | searchVal = null; 669 | sbTemp.Length = 0; 670 | } 671 | var srchExp = new FilterExpression(fieldName, searchVal, comparisonOp, matchType, isNot); 672 | parentSrchExpGrp.Add(curBoolOp, srchExp); 673 | } 674 | } 675 | 676 | private static void throwInvalidFilterConstruct(string filter, int pos) 677 | { 678 | // backup a little for context 679 | for (int n = 0; n < 2 && pos > 0; n++) pos--; 680 | 681 | throw new FileDbException(string.Format(FileDbException.InvalidFilterConstruct, filter.Substring(pos)), 682 | FileDbExceptionsEnum.InvalidFilterConstruct); 683 | } 684 | 685 | // Note: we will parse the InClause values but we don't know the Field type at this time 686 | // so we will add the values as strings and convert them later (see FileDb.evaluate) 687 | // 688 | private static HashSet parseInVals(string inVals, MatchTypeEnum matchType) 689 | { 690 | var hashSet = new HashSet(); 691 | 692 | bool inString = false, 693 | isStringVal = false; // used to know if we must trim 694 | 695 | StringBuilder sb = new StringBuilder(100); 696 | 697 | // skip whitespace 698 | int pos = 0; 699 | while (pos < inVals.Length && char.IsWhiteSpace(inVals[pos])) pos++; 700 | 701 | for (; pos < inVals.Length; pos++) 702 | { 703 | char ch = inVals[pos]; 704 | 705 | if (ch == '\'') 706 | { 707 | if (inString) 708 | { 709 | // is this an escaped single quote? 710 | if (pos < inVals.Length - 1 && inVals[pos + 1] == '\'') 711 | { 712 | // it is escaped - skip it and add 713 | pos++; 714 | goto AddChar; 715 | } 716 | 717 | // not escaped - means end of string 718 | inString = false; 719 | } 720 | else 721 | { 722 | inString = isStringVal = true; 723 | } 724 | 725 | continue; 726 | } 727 | else if (ch == ',') 728 | { 729 | if (!inString) 730 | { 731 | // end of current value 732 | string val = sb.ToString(); 733 | if (isStringVal) 734 | { 735 | if (matchType == MatchTypeEnum.IgnoreCase) 736 | val = val.ToUpper(); 737 | } 738 | else // we can trim non-string values 739 | val = val.Trim(); 740 | 741 | hashSet.Add(val); 742 | sb.Length = 0; 743 | 744 | continue; 745 | } 746 | } 747 | 748 | AddChar: 749 | // only add the char if we should - a space should only be added if in a string 750 | if (!(ch == ' ' && !inString)) 751 | sb.Append(ch); 752 | } 753 | 754 | // add the last one 755 | if (sb.Length > 0) 756 | { 757 | string val = sb.ToString(); 758 | if (isStringVal) 759 | { 760 | if (matchType == MatchTypeEnum.IgnoreCase) 761 | val = val.ToUpper(); 762 | } 763 | else // we can trim non-string values 764 | val = val.Trim(); 765 | 766 | hashSet.Add(val); 767 | } 768 | 769 | return hashSet; 770 | } 771 | 772 | } 773 | } 774 | -------------------------------------------------------------------------------- /Src/HexEncoding.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) EzTools Software - All Rights Reserved 2 | * Released under Mozilla Public License 2.0 3 | * Written and maintained by Brett Goodman 4 | */ 5 | using System; 6 | using System.Text; 7 | 8 | namespace Utils 9 | { 10 | /// 11 | /// Summary description for HexEncoding. 12 | /// 13 | internal class HexEncoding 14 | { 15 | internal HexEncoding() 16 | { 17 | // 18 | // TODO: Add constructor logic here 19 | // 20 | } 21 | internal static int GetByteCount(string hexString) 22 | { 23 | int numHexChars = 0; 24 | char c; 25 | // remove all none A-F, 0-9, characters 26 | for (int i = 0; i < hexString.Length; i++) 27 | { 28 | c = hexString[i]; 29 | if (IsHexDigit(c)) 30 | numHexChars++; 31 | } 32 | // if odd number of characters, discard last character 33 | if (numHexChars % 2 != 0) 34 | { 35 | numHexChars--; 36 | } 37 | return numHexChars / 2; // 2 characters per byte 38 | } 39 | /// 40 | /// Creates a byte array from the hexadecimal string. Each two characters are combined 41 | /// to create one byte. First two hexadecimal characters become first byte in returned array. 42 | /// Non-hexadecimal characters are ignored. 43 | /// 44 | /// string to convert to byte array 45 | /// number of characters in string ignored 46 | /// byte array, in the same left-to-right order as the hexString 47 | internal static byte[] GetBytes(string hexString, out int discarded) 48 | { 49 | discarded = 0; 50 | string newString = ""; 51 | char c; 52 | // remove all none A-F, 0-9, characters 53 | for (int i = 0; i < hexString.Length; i++) 54 | { 55 | c = hexString[i]; 56 | if (IsHexDigit(c)) 57 | newString += c; 58 | else 59 | discarded++; 60 | } 61 | // if odd number of characters, discard last character 62 | if (newString.Length % 2 != 0) 63 | { 64 | discarded++; 65 | newString = newString.Substring(0, newString.Length - 1); 66 | } 67 | 68 | int byteLength = newString.Length / 2; 69 | byte[] bytes = new byte[byteLength]; 70 | string hex; 71 | int j = 0; 72 | for (int i = 0; i < bytes.Length; i++) 73 | { 74 | hex = new String(new Char[] { newString[j], newString[j + 1] }); 75 | bytes[i] = HexToByte(hex); 76 | j = j + 2; 77 | } 78 | return bytes; 79 | } 80 | internal static string ToString(byte[] bytes) 81 | { 82 | var sb = new StringBuilder(); ; 83 | for (int i = 0; i < bytes.Length; i++) 84 | { 85 | sb.Append(bytes[i].ToString("x2")); 86 | } 87 | return sb.ToString(); 88 | } 89 | /// 90 | /// Determines if given string is in proper hexadecimal string format 91 | /// 92 | /// 93 | /// 94 | internal static bool InHexFormat(string hexString) 95 | { 96 | bool hexFormat = true; 97 | 98 | foreach (char digit in hexString) 99 | { 100 | if (!IsHexDigit(digit)) 101 | { 102 | hexFormat = false; 103 | break; 104 | } 105 | } 106 | return hexFormat; 107 | } 108 | 109 | /// 110 | /// Returns true is c is a hexadecimal digit (A-F, a-f, 0-9) 111 | /// 112 | /// Character to test 113 | /// true if hex digit, false if not 114 | internal static bool IsHexDigit(Char c) 115 | { 116 | int numChar; 117 | int numA = Convert.ToInt32('A'); 118 | int num1 = Convert.ToInt32('0'); 119 | c = Char.ToUpper(c); 120 | numChar = Convert.ToInt32(c); 121 | if (numChar >= numA && numChar < (numA + 6)) 122 | return true; 123 | if (numChar >= num1 && numChar < (num1 + 10)) 124 | return true; 125 | return false; 126 | } 127 | /// 128 | /// Converts 1 or 2 character string into equivalant byte value 129 | /// 130 | /// 1 or 2 character string 131 | /// byte 132 | private static byte HexToByte(string hex) 133 | { 134 | if (hex.Length > 2 || hex.Length <= 0) 135 | throw new ArgumentException("hex must be 1 or 2 characters in length"); 136 | byte newByte = byte.Parse(hex, System.Globalization.NumberStyles.HexNumber); 137 | return newByte; 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /Src/RijndaelEncryption.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Security.Cryptography; 4 | 5 | 6 | namespace FileDbNs 7 | { 8 | public class RijndaelEncryptor : IEncryptor 9 | { 10 | //static byte[] Salt = { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 }; 11 | 12 | Rijndael _encryptor; 13 | 14 | /// 15 | /// Constructor taking a key (password) and salt as a string 16 | /// 17 | /// The password 18 | /// Salt 19 | /// 20 | public RijndaelEncryptor(string encryptionKey, string salt) 21 | { 22 | init(encryptionKey, getSaltBytes(salt), null); 23 | } 24 | 25 | /// 26 | /// Constructor taking a key (password) and salt as byte[] 27 | /// 28 | /// The password 29 | /// Salt 30 | /// 31 | public RijndaelEncryptor(string encryptionKey, byte[] salt) 32 | { 33 | init(encryptionKey, salt, null); 34 | } 35 | 36 | /// 37 | /// Constructor taking a key (password) and salt as a string 38 | /// 39 | /// The password 40 | /// Salt 41 | /// Initialization Vector 42 | /// 43 | public RijndaelEncryptor(string encryptionKey, string salt, byte[] iv) 44 | { 45 | init(encryptionKey, getSaltBytes(salt), iv); 46 | } 47 | 48 | /// 49 | /// Constructor taking a key (password) and salt as a byte[] 50 | /// 51 | /// The password 52 | /// Salt 53 | /// Initialization Vector 54 | /// 55 | public RijndaelEncryptor(string encryptionKey, byte[] salt, byte[] iv) 56 | { 57 | init(encryptionKey, salt, iv); 58 | } 59 | 60 | void init(string encryptionKey, byte[] salt, byte[] iv) 61 | { 62 | _encryptor = Rijndael.Create(); 63 | PasswordDeriveBytes pdb = new PasswordDeriveBytes(encryptionKey, salt); 64 | _encryptor.Key = pdb.GetBytes(32); 65 | if(iv == null) 66 | iv = pdb.GetBytes(16); 67 | _encryptor.IV = iv; 68 | } 69 | 70 | byte[] getSaltBytes(string salt) 71 | { 72 | byte[] saltBytes; 73 | 74 | // Get the salt 75 | if (string.IsNullOrEmpty(salt)) 76 | { 77 | // no salt 78 | saltBytes = new byte[8]; 79 | for (int n = 0; n < 8; n++) 80 | saltBytes[n] = 0xff; 81 | } 82 | else 83 | saltBytes = System.Text.Encoding.Unicode.GetBytes(salt); 84 | 85 | return saltBytes; 86 | } 87 | 88 | /// 89 | /// Encrypt the passed byte array 90 | /// 91 | /// The data to encrypt 92 | /// The encrypted data 93 | /// 94 | public byte[] Encrypt(byte[] dataToEncrypt) 95 | { 96 | // Create a MemoryStream to accept the encrypted bytes 97 | MemoryStream ms = new MemoryStream(); 98 | CryptoStream cs = new CryptoStream(ms, _encryptor.CreateEncryptor(), CryptoStreamMode.Write); 99 | 100 | // Write the data and make it do the encryption 101 | cs.Write(dataToEncrypt, 0, dataToEncrypt.Length); 102 | 103 | // Close the crypto stream (or do FlushFinalBlock). 104 | // This will tell it that we have done our encryption and 105 | // there is no more data coming in, 106 | // and it is now a good time to apply the padding and 107 | // finalize the encryption process. 108 | cs.Close(); 109 | 110 | // Now get the encrypted data from the MemoryStream. 111 | // Some people make a mistake of using GetBuffer() here, 112 | // which is not the right way. 113 | byte[] encryptedData = ms.ToArray(); 114 | 115 | return encryptedData; 116 | } 117 | 118 | /// 119 | /// Decrypt the passed byte array 120 | /// 121 | /// The data to decrypt 122 | /// The decrypted data 123 | /// 124 | public byte[] Decrypt(byte[] encryptedData) 125 | { 126 | MemoryStream ms = new MemoryStream(); 127 | CryptoStream cs = new CryptoStream(ms, _encryptor.CreateDecryptor(), CryptoStreamMode.Write); 128 | 129 | // Write the data and make it do the decryption 130 | cs.Write(encryptedData, 0, encryptedData.Length); 131 | 132 | // Close the crypto stream (or do FlushFinalBlock). 133 | // This will tell it that we have done our decryption 134 | // and there is no more data coming in, 135 | // and it is now a good time to remove the padding 136 | // and finalize the decryption process. 137 | cs.Close(); 138 | 139 | // Now get the decrypted data from the MemoryStream. 140 | // Some people make a mistake of using GetBuffer() here, 141 | // which is not the right way. 142 | byte[] decryptedData = ms.ToArray(); 143 | 144 | return decryptedData; 145 | } 146 | #if false 147 | // From 148 | // https://www.codeproject.com/Articles/5719/Simple-encrypting-and-decrypting-data-in-C# 149 | // 150 | 151 | // Encrypt a byte array into a byte array using a key and an IV 152 | public static byte[] Encrypt(byte[] clearData, byte[] key, byte[] iv) 153 | { 154 | // Create a MemoryStream to accept the encrypted bytes 155 | MemoryStream ms = new MemoryStream(); 156 | 157 | // Create a symmetric algorithm. 158 | // We are going to use Rijndael because it is strong and 159 | // available on all platforms. 160 | // You can use other algorithms, to do so substitute the 161 | // next line with something like 162 | // TripleDES alg = TripleDES.Create(); 163 | Rijndael alg = Rijndael.Create(); 164 | 165 | // Now set the key and the IV. 166 | // We need the IV (Initialization Vector) because 167 | // the algorithm is operating in its default 168 | // mode called CBC (Cipher Block Chaining). 169 | // The IV is XORed with the first block (8 byte) 170 | // of the data before it is encrypted, and then each 171 | // encrypted block is XORed with the 172 | // following block of plaintext. 173 | // This is done to make encryption more secure. 174 | 175 | // There is also a mode called ECB which does not need an IV, 176 | // but it is much less secure. 177 | alg.Key = key; 178 | alg.IV = iv; 179 | 180 | // Create a CryptoStream through which we are going to be 181 | // pumping our data. 182 | // CryptoStreamMode.Write means that we are going to be 183 | // writing data to the stream and the output will be written 184 | // in the MemoryStream we have provided. 185 | CryptoStream cs = new CryptoStream(ms, alg.CreateEncryptor(), CryptoStreamMode.Write); 186 | 187 | // Write the data and make it do the encryption 188 | cs.Write(clearData, 0, clearData.Length); 189 | 190 | // Close the crypto stream (or do FlushFinalBlock). 191 | // This will tell it that we have done our encryption and 192 | // there is no more data coming in, 193 | // and it is now a good time to apply the padding and 194 | // finalize the encryption process. 195 | cs.Close(); 196 | 197 | // Now get the encrypted data from the MemoryStream. 198 | // Some people make a mistake of using GetBuffer() here, 199 | // which is not the right way. 200 | byte[] encryptedData = ms.ToArray(); 201 | 202 | return encryptedData; 203 | } 204 | 205 | // Encrypt a string into a string using a password 206 | // Uses Encrypt(byte[], byte[], byte[]) 207 | 208 | public static string Encrypt(string clearText, string password) 209 | { 210 | // First we need to turn the input string into a byte array. 211 | byte[] clearBytes = System.Text.Encoding.Unicode.GetBytes(clearText); 212 | 213 | // Then, we need to turn the password into Key and IV 214 | // We are using salt to make it harder to guess our key 215 | // using a dictionary attack - 216 | // trying to guess a password by enumerating all possible words. 217 | PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, Salt); 218 | 219 | // Now get the key/IV and do the encryption using the 220 | // function that accepts byte arrays. 221 | // Using PasswordDeriveBytes object we are first getting 222 | // 32 bytes for the Key 223 | // (the default Rijndael key length is 256bit = 32bytes) 224 | // and then 16 bytes for the IV. 225 | // IV should always be the block size, which is by default 226 | // 16 bytes (128 bit) for Rijndael. 227 | // If you are using DES/TripleDES/RC2 the block size is 228 | // 8 bytes and so should be the IV size. 229 | // You can also read KeySize/BlockSize properties off 230 | // the algorithm to find out the sizes. 231 | byte[] encryptedData = Encrypt(clearBytes, pdb.GetBytes(32), pdb.GetBytes(16)); 232 | 233 | // Now we need to turn the resulting byte array into a string. 234 | // A common mistake would be to use an Encoding class for that. 235 | //It does not work because not all byte values can be 236 | // represented by characters. 237 | // We are going to be using Base64 encoding that is designed 238 | //exactly for what we are trying to do. 239 | return Convert.ToBase64String(encryptedData); 240 | 241 | } 242 | 243 | // Encrypt bytes into bytes using a password 244 | // Uses Encrypt(byte[], byte[], byte[]) 245 | 246 | public static byte[] Encrypt(byte[] clearData, string password) 247 | { 248 | // We need to turn the password into Key and IV. 249 | // We are using salt to make it harder to guess our key 250 | // using a dictionary attack - 251 | // trying to guess a password by enumerating all possible words. 252 | PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, Salt); 253 | 254 | // Now get the key/IV and do the encryption using the function 255 | // that accepts byte arrays. 256 | // Using PasswordDeriveBytes object we are first getting 257 | // 32 bytes for the Key 258 | // (the default Rijndael key length is 256bit = 32bytes) 259 | // and then 16 bytes for the IV. 260 | // IV should always be the block size, which is by default 261 | // 16 bytes (128 bit) for Rijndael. 262 | // If you are using DES/TripleDES/RC2 the block size is 8 263 | // bytes and so should be the IV size. 264 | // You can also read KeySize/BlockSize properties off the 265 | // algorithm to find out the sizes. 266 | return Encrypt(clearData, pdb.GetBytes(32), pdb.GetBytes(16)); 267 | 268 | } 269 | 270 | // Encrypt a file into another file using a password 271 | public static void Encrypt(string fileIn, string fileOut, string password) 272 | { 273 | 274 | // First we are going to open the file streams 275 | FileStream fsIn = new FileStream(fileIn, 276 | FileMode.Open, FileAccess.Read); 277 | FileStream fsOut = new FileStream(fileOut, 278 | FileMode.OpenOrCreate, FileAccess.Write); 279 | 280 | // Then we are going to derive a Key and an IV from the 281 | // Password and create an algorithm 282 | PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, Salt); 283 | 284 | Rijndael alg = Rijndael.Create(); 285 | alg.Key = pdb.GetBytes(32); 286 | alg.IV = pdb.GetBytes(16); 287 | 288 | // Now create a crypto stream through which we are going 289 | // to be pumping data. 290 | // Our fileOut is going to be receiving the encrypted bytes. 291 | CryptoStream cs = new CryptoStream(fsOut, 292 | alg.CreateEncryptor(), CryptoStreamMode.Write); 293 | 294 | // Now will will initialize a buffer and will be processing 295 | // the input file in chunks. 296 | // This is done to avoid reading the whole file (which can 297 | // be huge) into memory. 298 | int bufferLen = 4096; 299 | byte[] buffer = new byte[bufferLen]; 300 | int bytesRead; 301 | 302 | do 303 | { 304 | // read a chunk of data from the input file 305 | bytesRead = fsIn.Read(buffer, 0, bufferLen); 306 | 307 | // encrypt it 308 | cs.Write(buffer, 0, bytesRead); 309 | } while (bytesRead != 0); 310 | 311 | // close everything 312 | 313 | // this will also close the unrelying fsOut stream 314 | cs.Close(); 315 | fsIn.Close(); 316 | } 317 | 318 | // Decrypt a byte array into a byte array using a key and an IV 319 | public static byte[] Decrypt(byte[] cipherData, byte[] Key, byte[] IV) 320 | { 321 | // Create a MemoryStream that is going to accept the 322 | // decrypted bytes 323 | MemoryStream ms = new MemoryStream(); 324 | 325 | // Create a symmetric algorithm. 326 | // We are going to use Rijndael because it is strong and 327 | // available on all platforms. 328 | // You can use other algorithms, to do so substitute the next 329 | // line with something like 330 | // TripleDES alg = TripleDES.Create(); 331 | Rijndael alg = Rijndael.Create(); 332 | 333 | // Now set the key and the IV. 334 | // We need the IV (Initialization Vector) because the algorithm 335 | // is operating in its default 336 | // mode called CBC (Cipher Block Chaining). The IV is XORed with 337 | // the first block (8 byte) 338 | // of the data after it is decrypted, and then each decrypted 339 | // block is XORed with the previous 340 | // cipher block. This is done to make encryption more secure. 341 | // There is also a mode called ECB which does not need an IV, 342 | // but it is much less secure. 343 | alg.Key = Key; 344 | alg.IV = IV; 345 | 346 | // Create a CryptoStream through which we are going to be 347 | // pumping our data. 348 | // CryptoStreamMode.Write means that we are going to be 349 | // writing data to the stream 350 | // and the output will be written in the MemoryStream 351 | // we have provided. 352 | CryptoStream cs = new CryptoStream(ms, alg.CreateDecryptor(), CryptoStreamMode.Write); 353 | 354 | // Write the data and make it do the decryption 355 | cs.Write(cipherData, 0, cipherData.Length); 356 | 357 | // Close the crypto stream (or do FlushFinalBlock). 358 | // This will tell it that we have done our decryption 359 | // and there is no more data coming in, 360 | // and it is now a good time to remove the padding 361 | // and finalize the decryption process. 362 | cs.Close(); 363 | 364 | // Now get the decrypted data from the MemoryStream. 365 | // Some people make a mistake of using GetBuffer() here, 366 | // which is not the right way. 367 | byte[] decryptedData = ms.ToArray(); 368 | 369 | return decryptedData; 370 | } 371 | 372 | // Decrypt a string into a string using a password 373 | // Uses Decrypt(byte[], byte[], byte[]) 374 | 375 | public static string Decrypt(string cipherText, string password) 376 | { 377 | // First we need to turn the input string into a byte array. 378 | // We presume that Base64 encoding was used 379 | byte[] cipherBytes = Convert.FromBase64String(cipherText); 380 | 381 | // Then, we need to turn the password into Key and IV 382 | // We are using salt to make it harder to guess our key 383 | // using a dictionary attack - 384 | // trying to guess a password by enumerating all possible words. 385 | PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, Salt); 386 | 387 | // Now get the key/IV and do the decryption using 388 | // the function that accepts byte arrays. 389 | // Using PasswordDeriveBytes object we are first 390 | // getting 32 bytes for the Key 391 | // (the default Rijndael key length is 256bit = 32bytes) 392 | // and then 16 bytes for the IV. 393 | // IV should always be the block size, which is by 394 | // default 16 bytes (128 bit) for Rijndael. 395 | // If you are using DES/TripleDES/RC2 the block size is 396 | // 8 bytes and so should be the IV size. 397 | // You can also read KeySize/BlockSize properties off 398 | // the algorithm to find out the sizes. 399 | byte[] decryptedData = Decrypt(cipherBytes, pdb.GetBytes(32), pdb.GetBytes(16)); 400 | 401 | // Now we need to turn the resulting byte array into a string. 402 | // A common mistake would be to use an Encoding class for that. 403 | // It does not work 404 | // because not all byte values can be represented by characters. 405 | // We are going to be using Base64 encoding that is 406 | // designed exactly for what we are trying to do. 407 | return System.Text.Encoding.Unicode.GetString(decryptedData); 408 | } 409 | 410 | // Decrypt bytes into bytes using a password 411 | // Uses Decrypt(byte[], byte[], byte[]) 412 | 413 | public static byte[] Decrypt(byte[] cipherData, string password) 414 | { 415 | // We need to turn the password into Key and IV. 416 | // We are using salt to make it harder to guess our key 417 | // using a dictionary attack - 418 | // trying to guess a password by enumerating all possible words. 419 | PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, Salt); 420 | 421 | // Now get the key/IV and do the Decryption using the 422 | //function that accepts byte arrays. 423 | // Using PasswordDeriveBytes object we are first getting 424 | // 32 bytes for the Key 425 | // (the default Rijndael key length is 256bit = 32bytes) 426 | // and then 16 bytes for the IV. 427 | // IV should always be the block size, which is by default 428 | // 16 bytes (128 bit) for Rijndael. 429 | // If you are using DES/TripleDES/RC2 the block size is 430 | // 8 bytes and so should be the IV size. 431 | 432 | // You can also read KeySize/BlockSize properties off the 433 | // algorithm to find out the sizes. 434 | return Decrypt(cipherData, pdb.GetBytes(32), pdb.GetBytes(16)); 435 | } 436 | 437 | // Decrypt a file into another file using a password 438 | public static void Decrypt(string fileIn, string fileOut, string password) 439 | { 440 | 441 | // First we are going to open the file streams 442 | FileStream fsIn = new FileStream(fileIn, 443 | FileMode.Open, FileAccess.Read); 444 | FileStream fsOut = new FileStream(fileOut, 445 | FileMode.OpenOrCreate, FileAccess.Write); 446 | 447 | // Then we are going to derive a Key and an IV from 448 | // the Password and create an algorithm 449 | PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, Salt); 450 | Rijndael alg = Rijndael.Create(); 451 | 452 | alg.Key = pdb.GetBytes(32); 453 | alg.IV = pdb.GetBytes(16); 454 | 455 | // Now create a crypto stream through which we are going 456 | // to be pumping data. 457 | // Our fileOut is going to be receiving the Decrypted bytes. 458 | CryptoStream cs = new CryptoStream(fsOut, 459 | alg.CreateDecryptor(), CryptoStreamMode.Write); 460 | 461 | // Now will will initialize a buffer and will be 462 | // processing the input file in chunks. 463 | // This is done to avoid reading the whole file (which can be 464 | // huge) into memory. 465 | int bufferLen = 4096; 466 | byte[] buffer = new byte[bufferLen]; 467 | int bytesRead; 468 | 469 | do 470 | { 471 | // read a chunk of data from the input file 472 | bytesRead = fsIn.Read(buffer, 0, bufferLen); 473 | 474 | // Decrypt it 475 | cs.Write(buffer, 0, bytesRead); 476 | 477 | } while (bytesRead != 0); 478 | 479 | // close everything 480 | cs.Close(); // this will also close the unrelying fsOut stream 481 | fsIn.Close(); 482 | } 483 | #endif 484 | } 485 | } -------------------------------------------------------------------------------- /Src/Table.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) EzTools Software - All Rights Reserved 2 | * Released under Mozilla Public License 2.0 3 | * Written and maintained by Brett Goodman 4 | */ 5 | using System; 6 | using System.Text; 7 | using System.Collections; 8 | using System.Collections.Generic; 9 | 10 | #if !SILVERLIGHT && !WINDOWS_PHONE 11 | namespace LINQPad 12 | { 13 | public interface ICustomMemberProvider 14 | { 15 | // Each of these methods must return a sequence 16 | // with the same number of elements: 17 | IEnumerable GetNames(); 18 | IEnumerable GetTypes(); 19 | IEnumerable GetValues(); 20 | } 21 | } 22 | #endif 23 | 24 | namespace FileDbNs 25 | { 26 | //===================================================================== 27 | /// 28 | /// Represents a column of the database (table). 29 | /// 30 | public class Field // : ICloneable - not available in Silverlight 31 | { 32 | /// 33 | /// Use this constructor when creating a new database. The ordinal index of the field will be the 34 | /// order it was added to the field list, unless a primary key field is specified and wasn't the 35 | /// first in the list. In this case the primary key field will be moved to the first in the list 36 | /// so that its always first. 37 | /// 38 | /// The name of the field 39 | /// The data type of the field 40 | /// 41 | public Field(string name, DataTypeEnum type) 42 | : this(name, type, -1) 43 | { 44 | } 45 | 46 | /// 47 | /// Use this constructor when you need to create a Records list manually rather than 48 | /// using one retured from a query. In this case, you must set the field ordinal index 49 | /// for each field, starting at zero. 50 | /// 51 | /// The name of the field 52 | /// The data type of the field 53 | /// The zero-based ordinal index of the field 54 | /// 55 | public Field(string name, DataTypeEnum type, int ordinal) 56 | { 57 | Name = name; 58 | DataType = type; 59 | AutoIncStart = null; // default to no AutoInc 60 | this.Ordinal = ordinal; 61 | } 62 | 63 | public override string ToString() 64 | { 65 | return Name; 66 | } 67 | 68 | /// 69 | /// The name of the field. 70 | /// 71 | public string Name { get; set; } 72 | 73 | /// 74 | /// The type of the field. 75 | /// 76 | public DataTypeEnum DataType { get; set; } 77 | 78 | /// 79 | /// The zero-based ordinal index of the field. 80 | /// 81 | public Int32 Ordinal { get; internal set; } 82 | 83 | /// 84 | /// Indicates if this is the one and only primary key field 85 | /// 86 | public bool IsPrimaryKey { get; set; } 87 | 88 | /// 89 | /// Indicate if this is an Array type field 90 | /// 91 | public bool IsArray { get; set; } 92 | 93 | /// 94 | /// Used for auto-increment fields. Set to the number which you want incrementing to begin. 95 | /// Leave it null if not an auto-increment field. 96 | /// 97 | public Int32? AutoIncStart { get; set; } 98 | 99 | internal Int32? CurAutoIncVal { get; set; } 100 | 101 | /// 102 | /// Returns true if this is an auto-increment field, false otherwise 103 | /// 104 | public bool IsAutoInc 105 | { 106 | get { return AutoIncStart.HasValue; } 107 | } 108 | 109 | /// 110 | /// Comment for the field 111 | /// 112 | public string Comment { get; set; } 113 | 114 | /// 115 | /// User property to associate a value with this Field 116 | /// 117 | public object Tag { get; set; } 118 | 119 | /// 120 | /// Clone this Field 121 | /// 122 | /// A new Field with the same values 123 | public Field Clone() 124 | { 125 | Field newField = new Field(this.Name, this.DataType, this.Ordinal); 126 | 127 | newField.AutoIncStart = this.AutoIncStart; 128 | newField.CurAutoIncVal = this.CurAutoIncVal; 129 | newField.IsArray = this.IsArray; 130 | newField.IsPrimaryKey = this.IsPrimaryKey; 131 | newField.Comment = this.Comment; 132 | newField.Tag = this.Tag; 133 | 134 | return newField; 135 | } 136 | } 137 | 138 | //===================================================================== 139 | /// 140 | /// Represents data for a row, a Record consists of name-value pairs. 141 | /// Used when adding data to the database and by the Record object 142 | /// to store data returned from queries. 143 | /// 144 | 145 | public class FieldValues : Dictionary // note: switch to NameValueCollection someday when added to Silverlight/WP 146 | { 147 | public FieldValues() : base(StringComparer.OrdinalIgnoreCase) { } 148 | public FieldValues(Int32 count) : base(count, StringComparer.OrdinalIgnoreCase) { } 149 | 150 | public new object this[string idx] 151 | { 152 | get 153 | { 154 | return base[idx]; 155 | } 156 | 157 | set 158 | { 159 | base[idx] = value; 160 | } 161 | } 162 | 163 | public new void Add(string fieldName, object value) 164 | { 165 | base.Add(fieldName, value); 166 | } 167 | 168 | public new bool ContainsKey(string fieldName) 169 | { 170 | return base.ContainsKey(fieldName); 171 | } 172 | 173 | public object GetValueOrNull(string fieldName) 174 | { 175 | return base.ContainsKey(fieldName) ? base[fieldName] : null; 176 | } 177 | } 178 | 179 | //===================================================================== 180 | /// 181 | /// Represents a single row returned from a query. It will 182 | /// contain one or more fields of the database (table). The last column may be the index of the row (if requested), 183 | /// which is the zero-based position of the record in the index. If there is no primary key specified for 184 | /// the database (table), then the index is the record number in the order in which the row was added to the database. 185 | /// 186 | /// 187 | public class Record : IEnumerable 188 | #if !SILVERLIGHT && !WINDOWS_PHONE 189 | , LINQPad.ICustomMemberProvider 190 | #endif 191 | { 192 | Fields _fields; 193 | List _values; 194 | 195 | // experimenting with dymanics 196 | //public dynamic DynamicFields; 197 | 198 | public Record(Fields fields) : this(fields, (object[]) null) 199 | { 200 | } 201 | 202 | /// 203 | /// Create a Record object with the indicated Fields and values. If creating a list of Record objects 204 | /// (for a Records list) be sure to use the same Fields list for each Record. 205 | /// 206 | /// List of Field objects 207 | /// Array of values. Each value will be converted to the Field type if possible. 208 | /// 209 | public Record(Fields fields, object[] values) 210 | { 211 | _fields = fields; 212 | _values = new List(fields.Count); 213 | 214 | for (int n = 0; n < fields.Count; n++) 215 | { 216 | Field field = fields[n]; 217 | object val = null; 218 | if (values != null) 219 | { 220 | val = values[n]; 221 | 222 | if (val != null && !field.IsArray) 223 | { 224 | // if the field is NOT an Array type and its NOT in the correct type, we must attempt convert it 225 | val = convertObjectToFieldType(val, field); 226 | } 227 | } 228 | 229 | _values.Add(val); 230 | } 231 | } 232 | 233 | /// 234 | /// Create a Record object with the indicated Fields and values. If creating a list of Record objects 235 | /// (for a Records list) be sure to use the same Fields list for each Record. 236 | /// 237 | /// List of Field objects 238 | /// Array of values. Each value will be converted to the Field type if possible. 239 | /// 240 | public Record(Fields fields, FieldValues values) 241 | { 242 | _fields = fields; 243 | _values = new List(fields.Count); 244 | 245 | // we must initialize the record values with null 246 | for (int n = 0; n < fields.Count; n++) 247 | _values.Add(null); 248 | 249 | if (values != null) 250 | { 251 | foreach (var val in values) 252 | { 253 | this[val.Key] = convertObjectToFieldType(val.Value, fields[val.Key]); 254 | } 255 | } 256 | } 257 | 258 | 259 | object convertObjectToFieldType(object data, Field field) 260 | { 261 | if (data == null) 262 | return null; 263 | 264 | object val = data; 265 | 266 | switch (field.DataType) 267 | { 268 | case DataTypeEnum.Byte: 269 | if (data.GetType() != typeof(Byte)) 270 | val = Convert.ToByte(data); 271 | break; 272 | 273 | case DataTypeEnum.Int32: 274 | if (data.GetType() != typeof(Int32)) 275 | val = Convert.ToInt32(data); 276 | break; 277 | 278 | case DataTypeEnum.UInt32: 279 | if (data.GetType() != typeof(UInt32)) 280 | val = Convert.ToUInt32(data); 281 | break; 282 | 283 | case DataTypeEnum.Float: 284 | if (data.GetType() != typeof(float)) 285 | val = Convert.ToSingle(data); 286 | break; 287 | 288 | case DataTypeEnum.Double: 289 | if (data.GetType() != typeof(double)) 290 | val = Convert.ToDouble(data); 291 | break; 292 | 293 | case DataTypeEnum.Bool: 294 | if (data.GetType() != typeof(bool)) 295 | { 296 | if (data is string) 297 | { 298 | var s = (string) data; 299 | if (string.Compare(s, "false", StringComparison.OrdinalIgnoreCase) == 0) 300 | val = false; 301 | else if (string.Compare(s, "true", StringComparison.OrdinalIgnoreCase) == 0) 302 | val = true; 303 | } 304 | else 305 | { 306 | try 307 | { 308 | Int32 i = Convert.ToInt32(data); 309 | val = i != 0; 310 | } 311 | catch //( Exception ) 312 | { 313 | throw new FileDbException(FileDbException.InValidBoolType, FileDbExceptionsEnum.InvalidDataType); 314 | } 315 | } 316 | } 317 | break; 318 | 319 | case DataTypeEnum.DateTime: 320 | if (data.GetType() != typeof(DateTime)) 321 | { 322 | if (data is string) 323 | val = DateTime.Parse(data.ToString()); 324 | else 325 | throw new FileDbException(FileDbException.InvalidDateTimeType, 326 | FileDbExceptionsEnum.InvalidDataType); 327 | } 328 | break; 329 | 330 | case DataTypeEnum.String: 331 | if (data.GetType() != typeof(String)) 332 | val = data.ToString(); 333 | break; 334 | 335 | case DataTypeEnum.Int64: 336 | if (data.GetType() != typeof(Int64)) 337 | val = Convert.ToInt64(data); 338 | break; 339 | 340 | case DataTypeEnum.Decimal: 341 | if (data.GetType() != typeof(Decimal)) 342 | { 343 | val = Convert.ToDecimal(data); 344 | } 345 | break; 346 | 347 | case DataTypeEnum.Guid: 348 | if (!(data is Guid)) 349 | { 350 | if (data is string) 351 | val = new Guid(data.ToString()); 352 | else 353 | throw new FileDbException(FileDbException.InvalidDateTimeType, FileDbExceptionsEnum.InvalidDataType); 354 | } 355 | break; 356 | 357 | default: 358 | // Unknown type 359 | throw new FileDbException(string.Format(FileDbException.StrInvalidDataType2, 360 | field.Name, field.DataType, data.GetType().Name), FileDbExceptionsEnum.InvalidDataType); 361 | } 362 | return val; 363 | } 364 | 365 | public override string ToString() 366 | { 367 | StringBuilder sb = new StringBuilder(); 368 | for (int n = 0; n < _fields.Count; n++) 369 | { 370 | var f = _fields[n]; 371 | if (sb.Length > 0) 372 | sb.Append("\r\n"); 373 | sb.Append(string.Format("{0}: {1}", f.Name, _values[n])); 374 | } 375 | return sb.ToString(); 376 | } 377 | 378 | // 379 | internal List Values 380 | { 381 | get { return _values; } 382 | } 383 | 384 | #region IEnumerable 385 | IEnumerator IEnumerable.GetEnumerator() 386 | { 387 | return (IEnumerator) GetEnumerator(); 388 | } 389 | 390 | public ObjectEnumerator GetEnumerator() 391 | { 392 | return new ObjectEnumerator(_values.ToArray()); 393 | } 394 | #endregion IEnumerable 395 | 396 | #region ICustomMemberProvider 397 | // for LinqPad 398 | #if !SILVERLIGHT && !WINDOWS_PHONE 399 | public IEnumerable GetNames() 400 | { 401 | var names = new List(_fields.Count); 402 | foreach (var f in _fields) 403 | { 404 | names.Add(f.Name); 405 | } 406 | return names; 407 | } 408 | public IEnumerable GetTypes() 409 | { 410 | var types = new List(_fields.Count); 411 | for (int n = 0; n < _fields.Count; n++) 412 | { 413 | Type type = null; 414 | Field f = _fields[0]; 415 | switch (f.DataType) 416 | { 417 | case DataTypeEnum.Bool: 418 | type = typeof(Boolean); 419 | break; 420 | case DataTypeEnum.Byte: 421 | type = typeof(Byte); 422 | break; 423 | case DataTypeEnum.Int32: 424 | type = typeof(Int32); 425 | break; 426 | case DataTypeEnum.UInt32: 427 | type = typeof(UInt32); 428 | break; 429 | case DataTypeEnum.Int64: 430 | type = typeof(Int64); 431 | break; 432 | case DataTypeEnum.Float: 433 | type = typeof(Single); 434 | break; 435 | //case DataTypeEnum.Single: 436 | // type = typeof( Single ); 437 | // break; 438 | case DataTypeEnum.Double: 439 | type = typeof(Double); 440 | break; 441 | case DataTypeEnum.Decimal: 442 | type = typeof(Double); 443 | break; 444 | case DataTypeEnum.DateTime: 445 | type = typeof(DateTime); 446 | break; 447 | case DataTypeEnum.String: 448 | type = typeof(String); 449 | break; 450 | } 451 | types.Add(type); 452 | } 453 | return types; 454 | } 455 | public IEnumerable GetValues() 456 | { 457 | var values = new List(_fields.Count); 458 | for (int n = 0; n < _fields.Count; n++) 459 | { 460 | values.Add(_values[n]); 461 | } 462 | return values; 463 | } 464 | #endif 465 | #endregion ICustomMemberProvider 466 | 467 | /* 468 | public void Add( string fieldName, object value ) 469 | { 470 | //int idx = _values.Count; 471 | _values.Add( value ); 472 | // we store the index of the value in the _values list 473 | //_record.Add( fieldName, idx ); 474 | }*/ 475 | 476 | public object this[string name] 477 | { 478 | get 479 | { 480 | /* now done in the Fields class 481 | if( !_record.ContainsKey( name ) ) 482 | throw new FileDbException( string.Format( FileDbException.InvalidFieldName, name ), FileDbExceptions.InvalidFieldName ); 483 | */ 484 | return _values[_fields[name].Ordinal]; 485 | } 486 | 487 | //////////////////////////////////////////// 488 | set 489 | { 490 | /* now done in the Fields class 491 | if( !_fields.ContainsKey( name ) ) 492 | throw new FileDbException( string.Format( FileDbException.InvalidFieldName, name ), FileDbExceptionsEnum.InvalidFieldName ); 493 | */ 494 | _values[_fields[name].Ordinal] = value; 495 | } 496 | } 497 | 498 | public object this[int idx] 499 | { 500 | get 501 | { 502 | if (idx >= 0 && idx < _values.Count) 503 | return _values[idx]; 504 | else 505 | throw new FileDbException(FileDbException.IndexOutOfRange, FileDbExceptionsEnum.IndexOutOfRange); 506 | } 507 | set 508 | { 509 | if (!(idx >= 0 && idx < _values.Count)) 510 | throw new FileDbException(FileDbException.IndexOutOfRange, FileDbExceptionsEnum.IndexOutOfRange); 511 | 512 | _values[idx] = value; 513 | } 514 | } 515 | 516 | /// 517 | /// The number of fields in the Record 518 | /// 519 | /// 520 | public int Length 521 | { 522 | get { return _values.Count; } 523 | } 524 | 525 | /// 526 | /// Tests to see if the indicated field is in this Record 527 | /// 528 | /// 529 | /// true if the field is in this Record 530 | /// 531 | public bool ContainsField(string fieldName) 532 | { 533 | return _fields.ContainsKey(fieldName); 534 | } 535 | 536 | public IList FieldNames 537 | { 538 | get 539 | { 540 | List fields = new List(_fields.Count); 541 | foreach (Field field in _fields) 542 | fields.Add(field.Name); 543 | return fields; 544 | } 545 | } 546 | 547 | public FieldValues GetFieldValues() 548 | { 549 | FieldValues fieldValues = new FieldValues(_fields.Count); 550 | foreach (Field field in _fields) 551 | { 552 | fieldValues.Add(field.Name, this[field.Name]); 553 | } 554 | return fieldValues; 555 | } 556 | 557 | /// 558 | /// A property which is used for integrating with the binding framework. 559 | /// 560 | /// 561 | public object Data 562 | { 563 | get 564 | { 565 | // when the binding framework reads this property, simply return the Record instance. The 566 | // RowIndexConverter takes care of extracting the correct property value 567 | return this; 568 | } 569 | 570 | set 571 | { 572 | // the RowIndexConverter will signal property changes by providing an instance of PropertyValueChange. 573 | FieldSetter setter = value as FieldSetter; 574 | this[setter.PropertyName] = setter.Value; 575 | } 576 | } 577 | 578 | #region Get helpers 579 | 580 | public bool IsNull(string fieldName) 581 | { 582 | return this[fieldName] == null; 583 | } 584 | 585 | public bool IsNull(int index) 586 | { 587 | return this[index] == null; 588 | } 589 | 590 | /// 591 | /// Return the Typed field value. 592 | /// 593 | /// The name of the field 594 | /// The Type field value 595 | /// 596 | public T GetValue(string fieldName) 597 | { 598 | return (T) this[fieldName]; 599 | } 600 | 601 | #region Deprecate these 602 | // see new ones below 603 | /// 604 | /// Return the integer field value. 605 | /// 606 | /// The name of the field 607 | /// The integer field value 608 | /// 609 | public Int32? GetInt32(string fieldName) 610 | { 611 | return (Int32?) this[fieldName]; 612 | } 613 | 614 | /// 615 | /// Return the integer field value. 616 | /// 617 | /// The ordinal index of the field 618 | /// The integer field value 619 | /// 620 | public Int32? GetInt32(int index) 621 | { 622 | return (Int32?) this[index]; 623 | } 624 | 625 | /// 626 | /// Return the unsigned integer field value. 627 | /// 628 | /// The name of the field 629 | /// The unsigned integer field value 630 | /// 631 | public UInt32? GetUInt32(string fieldName) 632 | { 633 | return (UInt32?) this[fieldName]; 634 | } 635 | 636 | /// 637 | /// Return the unsigned integer field value. 638 | /// 639 | /// The ordinal index of the field 640 | /// The unsigned integer field value 641 | /// 642 | public UInt32? GetUInt32(int index) 643 | { 644 | return (UInt32?) this[index]; 645 | } 646 | #endregion Deprecate these 647 | 648 | /// 649 | /// Return the integer field value. 650 | /// 651 | /// The name of the field 652 | /// The integer field value 653 | /// 654 | public Int32? GetInt(string fieldName) 655 | { 656 | return (Int32?) this[fieldName]; 657 | } 658 | 659 | /// 660 | /// Return the integer field value. 661 | /// 662 | /// The ordinal index of the field 663 | /// The integer field value 664 | /// 665 | public Int32? GetInt(int index) 666 | { 667 | return (Int32?) this[index]; 668 | } 669 | 670 | /// 671 | /// Return the unsigned integer field value. 672 | /// 673 | /// The name of the field 674 | /// The unsigned integer field value 675 | /// 676 | public UInt32? GetUInt(string fieldName) 677 | { 678 | return (UInt32?) this[fieldName]; 679 | } 680 | 681 | /// 682 | /// Return the unsigned integer field value. 683 | /// 684 | /// The ordinal index of the field 685 | /// The unsigned integer field value 686 | /// 687 | public UInt32? GetUInt(int index) 688 | { 689 | return (UInt32?) this[index]; 690 | } 691 | 692 | /// 693 | /// Return the long field value. 694 | /// 695 | /// The name of the field 696 | /// The long field value 697 | /// 698 | public Int64? GetLong(string fieldName) 699 | { 700 | return (Int64?) this[fieldName]; 701 | } 702 | 703 | /// 704 | /// Return the long field value. 705 | /// 706 | /// The ordinal index of the field 707 | /// The long field value 708 | /// 709 | public Int64? GetLong(int index) 710 | { 711 | return (Int64?) this[index]; 712 | } 713 | 714 | /* TODO 7-1-18: implement UInt64 type in the DB code 715 | /// 716 | /// Return the unsigned long field value. 717 | /// 718 | /// The name of the field 719 | /// The unsigned long field value 720 | /// 721 | public UInt64? GetULong(string fieldName) 722 | { 723 | return (UInt64?) this[fieldName]; 724 | } 725 | 726 | /// 727 | /// Return the unsigned long field value. 728 | /// 729 | /// The ordinal index of the field 730 | /// The unsigned long field value 731 | /// 732 | public UInt64? GetULong(int index) 733 | { 734 | return (UInt64?) this[index]; 735 | } 736 | */ 737 | 738 | /// 739 | /// Return the String field value. 740 | /// 741 | /// The name of the field 742 | /// The String field value 743 | /// 744 | public String GetString(string fieldName) 745 | { 746 | return (String) this[fieldName]; 747 | } 748 | 749 | /// 750 | /// Return the String field value. 751 | /// 752 | /// The ordinal index of the field 753 | /// The String field value 754 | /// 755 | public String GetString(int index) 756 | { 757 | return (String) this[index]; 758 | } 759 | 760 | /// 761 | /// Return the Byte field value. 762 | /// 763 | /// The name of the field 764 | /// The Byte field value 765 | /// 766 | public Byte? GetByte(string fieldName) 767 | { 768 | return (Byte?) this[fieldName]; 769 | } 770 | 771 | /// 772 | /// Return the Byte field value. 773 | /// 774 | /// The ordinal index of the field 775 | /// The Byte field value 776 | /// 777 | public Byte? GetByte(int index) 778 | { 779 | return (Byte?) this[index]; 780 | } 781 | 782 | /// 783 | /// Return the Single field value. 784 | /// 785 | /// The name of the field 786 | /// The Single field value 787 | /// 788 | public Single? GetSingle(string fieldName) 789 | { 790 | return (Single?) this[fieldName]; 791 | } 792 | 793 | /// 794 | /// Return the Single field value. 795 | /// 796 | /// The ordinal index of the field 797 | /// The Single field value 798 | /// 799 | public Single? GetSingle(int index) 800 | { 801 | return (Single?) this[index]; 802 | } 803 | 804 | /// 805 | /// Return the Double field value. 806 | /// 807 | /// The name of the field 808 | /// The Double field value 809 | /// 810 | public Double? GetDouble(string fieldName) 811 | { 812 | return (Double?) this[fieldName]; 813 | } 814 | 815 | /// 816 | /// Return the Decimal field value. 817 | /// 818 | /// The ordinal index of the field 819 | /// The Decimal field value 820 | /// 821 | public Decimal? GetDecimal(int index) 822 | { 823 | return (Decimal?) this[index]; 824 | } 825 | 826 | /// 827 | /// Return the Decimal field value. 828 | /// 829 | /// The name of the field 830 | /// The Decimal field value 831 | /// 832 | public Decimal? GetDecimal(string fieldName) 833 | { 834 | return (Decimal?) this[fieldName]; 835 | } 836 | 837 | /// 838 | /// Return the Double field value. 839 | /// 840 | /// The ordinal index of the field 841 | /// The Double field value 842 | /// 843 | public Double? GetDouble(int index) 844 | { 845 | return (Double?) this[index]; 846 | } 847 | 848 | /// 849 | /// Return the Boolean field value. 850 | /// 851 | /// The name of the field 852 | /// The Boolean field value 853 | /// 854 | public Boolean? GetBoolean(string fieldName) 855 | { 856 | return (Boolean?) this[fieldName]; 857 | } 858 | 859 | /// 860 | /// Return the Boolean field value. 861 | /// 862 | /// The ordinal index of the field 863 | /// The Boolean field value 864 | /// 865 | public Boolean? GetBoolean(int index) 866 | { 867 | return (Boolean?) this[index]; 868 | } 869 | 870 | /// 871 | /// Return the DateTime field value. 872 | /// 873 | /// The name of the field 874 | /// The DateTime field value 875 | /// 876 | public DateTime? GetDateTime(string fieldName) 877 | { 878 | return (DateTime?) this[fieldName]; 879 | } 880 | 881 | /// 882 | /// Return the DateTime field value. 883 | /// 884 | /// The ordinal index of the field 885 | /// The DateTime field value 886 | /// 887 | public DateTime? GetDateTime(int index) 888 | { 889 | return (DateTime?) this[index]; 890 | } 891 | #endregion Get helpers 892 | } 893 | 894 | //===================================================================== 895 | /// 896 | /// Used by the Record class for enumerating in foreach contructs 897 | /// 898 | /// 899 | public class ObjectEnumerator : IEnumerator 900 | { 901 | object[] _row; 902 | 903 | // Enumerators are positioned before the first element 904 | // until the first MoveNext() call. 905 | int position = -1; 906 | 907 | public ObjectEnumerator(object[] list) 908 | { 909 | _row = list; 910 | } 911 | 912 | public bool MoveNext() 913 | { 914 | position++; 915 | return (position < _row.Length); 916 | } 917 | 918 | public void Reset() 919 | { 920 | position = -1; 921 | } 922 | 923 | object IEnumerator.Current 924 | { 925 | get 926 | { 927 | return Current; 928 | } 929 | } 930 | 931 | public object Current 932 | { 933 | get 934 | { 935 | try 936 | { 937 | return _row[position]; 938 | } 939 | catch (IndexOutOfRangeException) 940 | { 941 | throw new InvalidOperationException(); 942 | } 943 | } 944 | } 945 | } 946 | 947 | //===================================================================== 948 | /// 949 | /// A List of Records 950 | /// 951 | /// 952 | public class Records : List 953 | { 954 | // allow users to create rows so they can add their own Records and set into DataGrid 955 | public Records() 956 | { 957 | } 958 | 959 | // allow users to create rows so they can add their own Records and set into DataGrid 960 | public Records(int capacity) : base(capacity) 961 | { 962 | } 963 | 964 | internal Records(Fields fields, object[][] records) 965 | : base(records.Length) 966 | { 967 | foreach (object[] record in records) 968 | { 969 | Record row = new Record(fields, record); 970 | this.Add(row); 971 | } 972 | } 973 | 974 | internal Records(Fields fields, object[] record) : base(1) 975 | { 976 | Record row = new Record(fields, record); 977 | this.Add(row); 978 | } 979 | } 980 | 981 | //===================================================================== 982 | /// 983 | /// A list of Fields 984 | /// 985 | /// 986 | public class Fields : List 987 | { 988 | Dictionary _fields; 989 | 990 | public Fields() 991 | { 992 | _fields = new Dictionary(StringComparer.OrdinalIgnoreCase); 993 | } 994 | 995 | public Fields(int capacity) 996 | : base(capacity) 997 | { 998 | _fields = new Dictionary(capacity, StringComparer.OrdinalIgnoreCase); 999 | } 1000 | 1001 | public Fields(Fields fields) 1002 | : base(fields.Count) 1003 | { 1004 | _fields = new Dictionary(fields.Count, StringComparer.OrdinalIgnoreCase); 1005 | foreach (Field field in fields) 1006 | { 1007 | this.Add(field); 1008 | } 1009 | } 1010 | 1011 | public new void Add(Field field) 1012 | { 1013 | base.Add(field); 1014 | _fields.Add(field.Name, field); 1015 | } 1016 | 1017 | public bool Remove(string fieldName) 1018 | { 1019 | bool ret = false; 1020 | Field field = _fields[fieldName]; 1021 | if (field != null) 1022 | { 1023 | ret = base.Remove(field); 1024 | _fields.Remove(fieldName); 1025 | } 1026 | return ret; 1027 | } 1028 | 1029 | public Field this[string fieldName] 1030 | { 1031 | get 1032 | { 1033 | if (!_fields.ContainsKey(fieldName)) 1034 | throw new FileDbException(string.Format(FileDbException.InvalidFieldName, fieldName), FileDbExceptionsEnum.InvalidFieldName); 1035 | 1036 | if (_fields.Count > 0) 1037 | return _fields[fieldName]; 1038 | else 1039 | return null; 1040 | } 1041 | } 1042 | 1043 | public bool ContainsKey(string fieldName) 1044 | { 1045 | return _fields.ContainsKey(fieldName); 1046 | } 1047 | } 1048 | 1049 | //===================================================================== 1050 | /// 1051 | /// Represents a data table returned from a query. A table is made up of Fields and Records. 1052 | /// 1053 | /// 1054 | public class Table : Records 1055 | { 1056 | const string Index = "index"; 1057 | Fields _fields; 1058 | 1059 | 1060 | /// 1061 | /// Create a table with the indicated Fields. 1062 | /// 1063 | /// The Fields list to use (a copy is made) 1064 | /// 1065 | public Table(Fields fields) 1066 | { 1067 | Create(fields, null, true); 1068 | } 1069 | 1070 | /// 1071 | /// Create a table with the indicated Fields and records. If copyFields is true, a new 1072 | /// Fields list is created and a copy of each field is made and its ordinal adjusted. 1073 | /// Otherwise the original Fields object is adopted. You should pass false for copyFields 1074 | /// only if you created the Fields list and its Field objects yourself. 1075 | /// 1076 | /// The Fields list to use 1077 | /// Indicates whether to make a copy of the Fields object and each Field. 1078 | /// 1079 | public Table(Fields fields, bool copyFields) 1080 | { 1081 | Create(fields, null, copyFields); 1082 | } 1083 | 1084 | /// 1085 | /// Create a table with the indicated Fields and records. If copyFields is true, a new 1086 | /// Fields list is created and a copy of each field is made and its ordinal adjusted. 1087 | /// Otherwise the original Fields object is adopted. You should pass false for copyFields 1088 | /// only if you created the Fields list and its Field objects yourself. 1089 | /// 1090 | /// The Fields list to use 1091 | /// The record data 1092 | /// Indicates whether to make a copy of the Fields object and each Field. 1093 | /// 1094 | public Table(Fields fields, object[][] records, bool copyFields) 1095 | { 1096 | Create(fields, records, copyFields); 1097 | } 1098 | 1099 | /// 1100 | /// Create a table with the indicated Fields and records. If copyFields is true, a new 1101 | /// Fields list is created and a copy of each field is made and its ordinal adjusted. 1102 | /// Otherwise the original Fields object is adopted. You should pass false for copyFields 1103 | /// only if you created the Fields list and its Field objects yourself and the Fields in 1104 | /// the Record objects match the data in the Record. 1105 | /// 1106 | /// The Fields list to use 1107 | /// The record data 1108 | /// Indicates whether to make a copy of the Fields object and each Field. 1109 | /// 1110 | public Table(Fields fields, Records records, bool copyFields) 1111 | : base(records.Count) 1112 | { 1113 | initFields(fields, copyFields); 1114 | 1115 | if (records != null) 1116 | { 1117 | foreach (Record record in records) 1118 | { 1119 | this.Add(record); 1120 | } 1121 | } 1122 | } 1123 | 1124 | internal void Create(Fields fields, object[][] records, bool copyFields) 1125 | { 1126 | this.Clear(); 1127 | 1128 | initFields(fields, copyFields); 1129 | 1130 | if (records != null) 1131 | { 1132 | // do what the Records constructor does 1133 | foreach (object[] record in records) 1134 | { 1135 | // very important to use member _fields NOT fields because ordinals have been adjusted 1136 | Record row = new Record(_fields, record); 1137 | this.Add(row); 1138 | } 1139 | } 1140 | } 1141 | 1142 | void initFields(Fields fields, bool copyFields) 1143 | { 1144 | if (copyFields) 1145 | { 1146 | _fields = new Fields(fields.Count); 1147 | // we must adjust the field ordinals 1148 | int n = 0; 1149 | foreach (Field field in fields) 1150 | { 1151 | Field f = (Field) field.Clone(); 1152 | f.Ordinal = n++; 1153 | _fields.Add(f); 1154 | } 1155 | } 1156 | else 1157 | { 1158 | _fields = fields; 1159 | } 1160 | } 1161 | 1162 | public override string ToString() 1163 | { 1164 | return string.Format("NumRecords = {0}", this.Count); 1165 | } 1166 | 1167 | /* experimenting with dymanics 1168 | * 1169 | public void SetDynamicFields() 1170 | { 1171 | foreach( var record in this ) 1172 | { 1173 | record.DynamicFields = new ExpandoObject(); 1174 | 1175 | var expando = (IDictionary) record.DynamicFields; 1176 | 1177 | for( int idx = 0; idx < record.FieldNames.Count; idx++ ) 1178 | { 1179 | expando.Add( record.FieldNames[idx], record.Values[idx] ); 1180 | } 1181 | } 1182 | }*/ 1183 | 1184 | //public Records Records { get { return _records; } } 1185 | 1186 | /// 1187 | /// The Fields of the Table 1188 | /// 1189 | /// 1190 | public Fields Fields { get { return _fields; } } 1191 | 1192 | /// 1193 | /// Add a new Record to this Table with all null values. 1194 | /// 1195 | /// 1196 | /// 1197 | public Record AddRecord() 1198 | { 1199 | var record = new Record(_fields, (object[]) null); 1200 | this.Add(record); 1201 | return record; 1202 | } 1203 | /// 1204 | /// Add a new Record to this Table with the specfied values, which must be in the order 1205 | /// of their corresponding fields. 1206 | /// 1207 | /// 1208 | /// 1209 | public Record AddRecord(object[] row) 1210 | { 1211 | var record = new Record(_fields, row); 1212 | this.Add(record); 1213 | return record; 1214 | } 1215 | 1216 | /// 1217 | /// Add a new Record to this Table with the FieldValues 1218 | /// 1219 | /// 1220 | /// 1221 | /// 1222 | public Record AddRecord(FieldValues values) 1223 | { 1224 | var record = new Record(_fields, values); 1225 | this.Add(record); 1226 | return record; 1227 | } 1228 | 1229 | #if NETFX_CORE || PCL 1230 | 1231 | /// 1232 | /// Save this Table to the Stream as a new database. If the Stream is null 1233 | /// one will be created and it will be a memory DB. The new database will be just as if you had created 1234 | /// it from scratch and populated it with the Table data. 1235 | /// 1236 | /// 1237 | public FileDb SaveToDb( Stream dataStrm ) 1238 | { 1239 | FileDb db = new FileDb(); 1240 | db.CreateFromTable( this, dataStrm ); 1241 | return db; 1242 | } 1243 | #else 1244 | /// 1245 | /// Save this Table to the indicated file as a new database. If the file exists 1246 | /// it will be overwritten. The new database will be just as if you had created 1247 | /// it from scratch and populated it with the Table data. 1248 | /// 1249 | /// The full path and filename of the new database 1250 | /// 1251 | public FileDb SaveToDb(string dbFileName) 1252 | { 1253 | FileDb db = new FileDb(); 1254 | db.CreateFromTable(this, dbFileName); 1255 | return db; 1256 | } 1257 | #endif 1258 | 1259 | #region SelectRecords 1260 | 1261 | #region String 1262 | 1263 | //---------------------------------------------------------------------------------------- 1264 | /// 1265 | /// Return a Table of Records filtered by the filter parameter. 1266 | /// 1267 | /// A string representing the desired filter, eg. LastName = 'Fuller' 1268 | /// A new Table with the requested Records 1269 | /// 1270 | public Table SelectRecords(string filter) 1271 | { 1272 | FilterExpressionGroup filterExpGrp = FilterExpressionGroup.Parse(filter); 1273 | return SelectRecords(filterExpGrp); 1274 | } 1275 | 1276 | //---------------------------------------------------------------------------------------- 1277 | /// 1278 | /// Return a Table of Records filtered by the filter parameter. Only the specified Fields 1279 | /// will be in the Table. 1280 | /// 1281 | /// A string representing the desired filter, eg. LastName = 'Fuller' 1282 | /// The desired fields to be in the returned Table 1283 | /// A new Table with the requested Records and Fields 1284 | /// 1285 | public Table SelectRecords(string filter, string[] fieldList) 1286 | { 1287 | FilterExpressionGroup filterExpGrp = FilterExpressionGroup.Parse(filter); 1288 | return SelectRecords(filterExpGrp, fieldList, null); 1289 | } 1290 | 1291 | //---------------------------------------------------------------------------------------- 1292 | /// 1293 | /// Return a Table of Records filtered by the filter parameter. Only the specified Fields 1294 | /// will be in the Table. 1295 | /// 1296 | /// A string representing the desired filter, eg. LastName = 'Fuller' 1297 | /// The desired fields to be in the returned Table 1298 | /// A list of one or more fields to order the returned table by, 1299 | /// or null for default order. If an orderByField is prefixed with "!", that field will sorted 1300 | /// in reverse order. 1301 | /// A new Table with the requested Records and Fields ordered by the specified list 1302 | /// 1303 | public Table SelectRecords(string filter, string[] fieldList, string[] orderByList) 1304 | { 1305 | FilterExpressionGroup filterExpGrp = FilterExpressionGroup.Parse(filter); 1306 | return SelectRecords(filterExpGrp, fieldList, orderByList); 1307 | } 1308 | #endregion String 1309 | 1310 | #region FilterExpression 1311 | 1312 | //---------------------------------------------------------------------------------------- 1313 | /// 1314 | /// Return a Table of Records filtered by the filter parameter. 1315 | /// 1316 | /// A FilterExpression representing the desired filter. 1317 | /// A new Table with the requested Records 1318 | /// 1319 | public Table SelectRecords(FilterExpression filter) 1320 | { 1321 | return SelectRecords(filter, null, null); 1322 | } 1323 | 1324 | //---------------------------------------------------------------------------------------- 1325 | /// 1326 | /// Return a Table of Records filtered by the filter parameter. Only the specified Fields 1327 | /// will be in the Table. 1328 | /// 1329 | /// A FilterExpression representing the desired filter. 1330 | /// The desired fields to be in the returned Table 1331 | /// A new Table with the requested Records and Fields 1332 | /// 1333 | public Table SelectRecords(FilterExpression filter, string[] fieldList) 1334 | { 1335 | return SelectRecords(filter, fieldList, null); 1336 | } 1337 | 1338 | //---------------------------------------------------------------------------------------- 1339 | /// 1340 | /// Return a Table of Records filtered by the filter parameter. Only the specified Fields 1341 | /// will be in the Table. 1342 | /// 1343 | /// A FilterExpression representing the desired filter. 1344 | /// The desired fields to be in the returned Table 1345 | /// A list of one or more fields to order the returned table by, 1346 | /// or null for default order. If an orderByField is prefixed with "!", that field will sorted 1347 | /// in reverse order. 1348 | /// A new Table with the requested Records and Fields ordered by the specified fields. 1349 | /// 1350 | public Table SelectRecords(FilterExpression filter, string[] fieldList, string[] orderByList) 1351 | { 1352 | FilterExpressionGroup filterExpGrp = new FilterExpressionGroup(); 1353 | filterExpGrp.Add(BoolOpEnum.And, filter); 1354 | 1355 | return SelectRecords(filterExpGrp, fieldList, orderByList); 1356 | } 1357 | #endregion FilterExpression 1358 | 1359 | #region FilterExpressionGroup 1360 | 1361 | //---------------------------------------------------------------------------------------- 1362 | /// 1363 | /// Return a Table of Records filtered by the filter parameter. 1364 | /// 1365 | /// A FilterExpressionGroup representing the desired filter. 1366 | /// A new Table with the requested Records 1367 | /// 1368 | public Table SelectRecords(FilterExpressionGroup filter) 1369 | { 1370 | return SelectRecords(filter, null, null); 1371 | } 1372 | 1373 | //---------------------------------------------------------------------------------------- 1374 | /// 1375 | /// Return a Table of Records filtered by the filter parameter. Only the specified Fields 1376 | /// will be in the Table. 1377 | /// 1378 | /// A FilterExpression representing the desired filter. 1379 | /// The desired fields to be in the returned Table 1380 | /// A new Table with the requested Records and Fields 1381 | /// 1382 | public Table SelectRecords(FilterExpressionGroup filter, string[] fieldList) 1383 | { 1384 | return SelectRecords(filter, fieldList, null); 1385 | } 1386 | 1387 | //---------------------------------------------------------------------------------------- 1388 | /// 1389 | /// Return a Table of Records filtered by the filter parameter. Only the specified Fields 1390 | /// will be in the Table. 1391 | /// 1392 | /// A FilterExpression representing the desired filter. 1393 | /// The desired fields to be in the returned Table 1394 | /// A list of one or more fields to order the returned table by, 1395 | /// or null for default order. If an orderByField is prefixed with "!", that field will sorted 1396 | /// in reverse order. 1397 | /// A new Table with the requested Records and Fields in the specified order 1398 | /// 1399 | public Table SelectRecords(FilterExpressionGroup filter, string[] fieldList, string[] orderByList) 1400 | { 1401 | Table table = null; 1402 | Fields fields = new Fields(_fields.Count); 1403 | int numFields = 0; 1404 | 1405 | if (fieldList != null) 1406 | { 1407 | numFields = fieldList.Length; 1408 | for (int n = 0; n < numFields; n++) 1409 | { 1410 | string fieldName = fieldList[n]; 1411 | // Check the orderby field name 1412 | if (!_fields.ContainsKey(fieldName)) 1413 | throw new Exception(string.Format("Invalid field name - {0}", fieldName)); 1414 | 1415 | Field existingField = _fields[fieldName]; 1416 | fields.Add(new Field(fieldName, existingField.DataType, n)); 1417 | } 1418 | } 1419 | else 1420 | { 1421 | numFields = _fields.Count; 1422 | for (int n = 0; n < numFields; n++) 1423 | { 1424 | Field existingField = _fields[n]; 1425 | fields.Add(new Field(existingField.Name, existingField.DataType, n)); 1426 | } 1427 | } 1428 | 1429 | Records records = new Records(Math.Min(10, this.Count)); 1430 | 1431 | foreach (Record record in this) 1432 | { 1433 | bool isMatch = FileDbEngine.Evaluate(filter, record.Values.ToArray(), _fields); 1434 | 1435 | if (isMatch) 1436 | { 1437 | object[] values = new object[numFields]; 1438 | 1439 | // get the values 1440 | 1441 | if (fieldList != null) 1442 | { 1443 | for (int n = 0; n < numFields; n++) 1444 | { 1445 | string fieldName = fieldList[n]; 1446 | object value = record[fieldName]; 1447 | values[n] = value; 1448 | } 1449 | } 1450 | else 1451 | { 1452 | for (int n = 0; n < numFields; n++) 1453 | { 1454 | object value = record[n]; 1455 | values[n] = value; 1456 | } 1457 | } 1458 | 1459 | Record newRecord = new Record(fields, values); 1460 | records.Add(newRecord); 1461 | } 1462 | } 1463 | 1464 | if (orderByList != null) 1465 | { 1466 | orderBy(records, fieldList, orderByList); 1467 | } 1468 | 1469 | table = new Table(fields, records, false); 1470 | 1471 | return table; 1472 | } 1473 | #endregion FilterExpressionGroup 1474 | 1475 | void orderBy(Records records, string[] fieldList, string[] orderByList) 1476 | { 1477 | List sortFields = new List(orderByList.Length); 1478 | List sortDirLst = new List(orderByList.Length); 1479 | List caseLst = new List(orderByList.Length); 1480 | 1481 | FileDbEngine.GetOrderByLists(_fields, fieldList, orderByList, sortFields, sortDirLst, caseLst); 1482 | 1483 | records.Sort(new RecordComparer(sortFields, sortDirLst, caseLst)); 1484 | } 1485 | 1486 | #endregion Select 1487 | 1488 | } 1489 | 1490 | /////////////////////////////////////////////////////////////////////// 1491 | #region RecordComparer 1492 | //===================================================================== 1493 | class RecordComparer : IComparer 1494 | { 1495 | List _fieldLst; 1496 | List _sortDirLst, 1497 | _caseLst; 1498 | 1499 | internal RecordComparer(List fieldLst, List sortDirLst, List caseLst) 1500 | { 1501 | _fieldLst = fieldLst; 1502 | _caseLst = caseLst; 1503 | _sortDirLst = sortDirLst; 1504 | } 1505 | 1506 | // Calls CaseInsensitiveComparer.Compare with the parameters reversed 1507 | 1508 | public int Compare(Record x, Record y) 1509 | { 1510 | Int32 nRet = 0; 1511 | object v1, v2; 1512 | Record row1 = x as Record, 1513 | row2 = y as Record; 1514 | 1515 | if (row1 == null || row2 == null) 1516 | return 0; 1517 | 1518 | for (int n = 0; n < _fieldLst.Count; n++) 1519 | { 1520 | Field field = _fieldLst[n]; 1521 | bool reverseSort = _sortDirLst[n]; 1522 | bool caseInsensitive = _caseLst[n]; 1523 | 1524 | v1 = row1[field.Ordinal]; 1525 | v2 = row2[field.Ordinal]; 1526 | 1527 | int compVal = FileDbEngine.CompareVals(v1, v2, field.DataType, caseInsensitive); 1528 | 1529 | if (reverseSort) 1530 | compVal = -compVal; 1531 | 1532 | // we go until we find mismatch 1533 | 1534 | if (compVal != 0) 1535 | { 1536 | nRet = compVal; 1537 | break; 1538 | } 1539 | } 1540 | 1541 | return nRet; 1542 | } 1543 | } 1544 | #endregion RecordComparer 1545 | 1546 | //===================================================================== 1547 | /// 1548 | /// A simple class used to communicate property value changes to a Record. 1549 | /// Used by WPF databinding. 1550 | /// 1551 | /// 1552 | public class FieldSetter 1553 | { 1554 | private string _propertyName; 1555 | 1556 | private object _value; 1557 | 1558 | public object Value 1559 | { 1560 | get { return _value; } 1561 | } 1562 | 1563 | public string PropertyName 1564 | { 1565 | get { return _propertyName; } 1566 | } 1567 | 1568 | public FieldSetter(string propertyName, object value) 1569 | { 1570 | _propertyName = propertyName; 1571 | _value = value; 1572 | } 1573 | } 1574 | } 1575 | -------------------------------------------------------------------------------- /Src/enums.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) EzTools Software - All Rights Reserved 2 | * Released under Mozilla Public License 2.0 3 | * Written and maintained by Brett Goodman 4 | */ 5 | using System; 6 | 7 | namespace FileDbNs 8 | { 9 | //===================================================================== 10 | /// 11 | /// Specifies the data type for database Fields 12 | /// 13 | /// 14 | internal enum DataTypeEnum_old : short 15 | { 16 | String = 0, Byte = 1, Int = 2, UInt = 3, Float = 4, Double = 5, Bool = 6, DateTime = 7, 17 | Int64 = 8, Decimal = 9, Guid = 10, Undefined = 0x7FFF 18 | } 19 | 20 | #if NETFX_CORE || PCL 21 | public enum TypeCode 22 | { 23 | Empty = 0, 24 | Object = 1, 25 | DBNull = 2, 26 | Boolean = 3, 27 | Char = 4, 28 | SByte = 5, 29 | Byte = 6, 30 | Int16 = 7, 31 | UInt16 = 8, 32 | Int32 = 9, 33 | UInt32 = 10, 34 | Int64 = 11, 35 | UInt64 = 12, 36 | Single = 13, 37 | Double = 14, 38 | Decimal = 15, 39 | DateTime = 16, 40 | String = 18, 41 | } 42 | #endif 43 | 44 | public enum DataTypeEnum : short 45 | { 46 | Bool = TypeCode.Boolean, 47 | Byte = TypeCode.Byte, 48 | Int = TypeCode.Int32, 49 | Int32 = TypeCode.Int32, 50 | UInt = TypeCode.UInt32, 51 | UInt32 = TypeCode.UInt32, 52 | Long = TypeCode.Int64, 53 | Int64 = TypeCode.Int64, 54 | Float = TypeCode.Single, 55 | Single = TypeCode.Single, 56 | Double = TypeCode.Double, 57 | Decimal = TypeCode.Decimal, 58 | DateTime = TypeCode.DateTime, 59 | String = TypeCode.String, 60 | Guid = 100, 61 | Undefined = 0x7FFF 62 | } 63 | 64 | #if false 65 | public enum FolderLocEnum 66 | { 67 | #if !WINDOWS_PHONE_APP 68 | Default, 69 | #else 70 | Default = 0, 71 | LocalFolder = 0, 72 | RoamingFolder, 73 | TempFolder 74 | #endif 75 | } 76 | #endif 77 | 78 | //===================================================================== 79 | /// 80 | /// Specifies the type of match for FilterExpressions with String data types 81 | /// 82 | public enum MatchTypeEnum { UseCase, IgnoreCase } 83 | 84 | //===================================================================== 85 | /// 86 | /// Specifies the comparison operator to use for FilterExpressions 87 | /// 88 | public enum ComparisonOperatorEnum { Equal, NotEqual, GreaterThan, GreaterThanOrEqual, LessThan, LessThanOrEqual, In, Regex, Contains } 89 | 90 | //===================================================================== 91 | /// 92 | /// Boolean operands to use to join FilterExpressions 93 | /// 94 | public enum BoolOpEnum { And, Or } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /Standard.Net/FileDb.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | true 6 | FileDb.snk 7 | 7.4.5.0 8 | 7.4.5.0 9 | FileDb.Standard 10 | Brett Goodman (EzTools Software) 11 | FileDb .NET Database Engine 12 | A portable NoSql database for .NET applications with a single table per file. The field types are native CLR types, and arrays of the same, eg: Int32, Boolean, Byte[]. Multiple databases can be joined using LINQ. 13 | EzTools Software (www.eztools-software.com) 14 | Copyright © EzTools Software - all rights reserved 15 | https://github.com/eztools-software/FileDb/blob/master/LICENSE 16 | https://github.com/eztools-software/FileDb 17 | https://github.com/eztools-software/FileDb 18 | Git 19 | FileDb Database NoSql Linq portable class library pcl xamarin android iphone mobile 20 | true 21 | false 22 | FileDb.Standard 23 | Fix for when searching for/with null values. eg. this would not work before: where FirstName <> null (<> or != both mean NOT EQUAL) 24 | en-AU 25 | 7.4.5 26 | 27 | 28 | 29 | TRACE 30 | 31 | 32 | 33 | D:\Dev\EzTools.NET\FileDb\trunk\Src\Standard.Net\FileDb.Standard.xml 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /Standard.Net/FileDb.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26430.6 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileDb", "FileDb.csproj", "{32E3EAA2-8B30-4207-A596-00F939B766BB}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {32E3EAA2-8B30-4207-A596-00F939B766BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {32E3EAA2-8B30-4207-A596-00F939B766BB}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {32E3EAA2-8B30-4207-A596-00F939B766BB}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {32E3EAA2-8B30-4207-A596-00F939B766BB}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /Standard.Net/FileDb.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eztools-software/FileDb/3ab718a4eb43d7396b9c88608338a1aeb4d84fe6/Standard.Net/FileDb.snk -------------------------------------------------------------------------------- /Standard.Net/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "FileDb": { 4 | "commandName": "Executable", 5 | "executablePath": "D:\\Dev\\EzTools.NET\\FileDb\\trunk\\Setup\\Files\\Samples\\WinForms\\bin\\Debug\\SampleApp.exe" 6 | } 7 | } 8 | } --------------------------------------------------------------------------------