├── .editorconfig ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── Bug_report.md │ ├── Feature_request.md │ └── Support.md ├── PULL_REQUEST_TEMPLATE.md └── stale.yml ├── .gitignore ├── Build ├── push.bat └── push.sh ├── Building.md ├── CodeAnalysisRules.ruleset ├── Insight.Database.Configuration ├── ConnectionStringSettingsExtensions.cs ├── Insight.Database.Configuration.csproj ├── Properties │ └── AssemblyInfo.cs └── ReliableConnectionExtensions.cs ├── Insight.Database.Core ├── CachedDbDataReader.cs ├── CodeGenerator │ ├── ClassDeserializerGenerator.cs │ ├── ClassPropInfo.cs │ ├── ColumnInfo.cs │ ├── DbParameterGenerator.cs │ ├── DbReaderDeserializer.cs │ ├── ExpandoGenerator.cs │ ├── IlHelper.cs │ ├── InterfaceGenerator.cs │ ├── ObjectListDbDataReader.cs │ ├── ObjectReader.cs │ ├── QueryIdentity.cs │ ├── SchemaIdentity.cs │ ├── SchemaMappingIdentity.cs │ ├── SchemaMappingType.cs │ ├── StaticFieldStorage.cs │ ├── TypeConverterGenerator.cs │ └── TypeHelper.cs ├── Compatibility.cs ├── CustomDictionary.xml ├── DbCommandWrapper.cs ├── DbConnectionWrapper.cs ├── DbConnectionWrapperProviderFactory.cs ├── DbDataReaderWrapper.cs ├── DynamicConnection.cs ├── Expando │ ├── ExpandoExtensions.cs │ └── FastExpando.cs ├── Extensions │ ├── AsyncExtensions.cs │ ├── AsyncExtensions.generated.cs │ ├── AsyncExtensions.tt │ ├── DBCommandExtensions.cs │ ├── DBConnectionExtensions.cs │ ├── DBConnectionExtensions.generated.cs │ ├── DBConnectionExtensions.tt │ ├── DbConnectionStringBuilderExtensions.cs │ ├── DbReaderExtensions.cs │ ├── DbReaderExtensions.generated.cs │ ├── DbReaderExtensions.tt │ └── IAsyncEnumerable.cs ├── GenericTypes.tt ├── GlobalSuppressions.cs ├── Helpers.cs ├── Insight.Database.Core.csproj ├── InsightBulkCopy.cs ├── Mapping │ ├── BindChildrenAttribute.cs │ ├── BindChildrenFor.cs │ ├── ColumnAttribute.cs │ ├── ColumnMapping.cs │ ├── DualMappingCollection.cs │ ├── FieldMapping.cs │ ├── IColumnMapper.cs │ ├── IDualMapper.cs │ ├── IMapper.cs │ ├── IMappingTransform.cs │ ├── IParameterDataTypeMapper.cs │ ├── IParameterMapper.cs │ ├── MappingCollection.cs │ └── RegexReplaceTransform.cs ├── MergeOutputAttribute.cs ├── Optimistic │ ├── OptimisticCommand.cs │ ├── OptimisticConcurrencyException.cs │ └── OptimisticConnection.cs ├── Parameters.cs ├── PlatformCompatibility │ ├── ApplicationHelpers.cs │ └── TypeExtensions.cs ├── Properties │ └── AssemblyInfo.cs ├── Providers │ ├── DbConnectionWrapperInsightDbProvider.cs │ ├── InsightDbProvider.cs │ └── WrappedInsightDbProvider.cs ├── ReflectionCompatibility.cs ├── Reliable │ ├── IRetryStrategy.cs │ ├── ReliableCommand.cs │ ├── ReliableConnection.cs │ ├── ReliableConnectionInsightDbProvider.cs │ ├── RetryEventArgs.cs │ └── RetryStrategy.cs ├── Serialization │ ├── BooleanSerializer.cs │ ├── DbObjectSerializer.cs │ ├── DbSerializationRule.cs │ ├── IDbObjectSerializer.cs │ ├── IDbSerializationRule.cs │ ├── JsonObjectSerializer.cs │ ├── SerializationMode.cs │ ├── ToStringObjectSerializer.cs │ └── XmlObjectSerializer.cs ├── SqlAttribute.cs ├── SqlConstructorAttribute.cs └── Structure │ ├── ChildMapper.cs │ ├── ChildMapperHelper.cs │ ├── ChildRecordReader.cs │ ├── ChildRecordsAttribute.cs │ ├── Children.cs │ ├── ColumnOverride.cs │ ├── CustomRecordReader.cs │ ├── DerivedResultsReader.cs │ ├── Guardian.cs │ ├── Guardian.generated.cs │ ├── Guardian.tt │ ├── IChildMapper.cs │ ├── IChildRecordReader.cs │ ├── IDAccessor.cs │ ├── IQueryReader.cs │ ├── IRecordIdAttribute.cs │ ├── IRecordReader.cs │ ├── IRecordStructure.cs │ ├── ListReader.cs │ ├── ListReader.generated.cs │ ├── ListReader.tt │ ├── MultiReader.cs │ ├── OneToOne.cs │ ├── OneToOne.generated.cs │ ├── OneToOne.tt │ ├── ParentAndChildReader.cs │ ├── ParentRecordIdAttribute.cs │ ├── PostProcessRecordReader.cs │ ├── Query.cs │ ├── Query.generated.cs │ ├── Query.tt │ ├── QueryReader.cs │ ├── RecordIdAttribute.cs │ ├── RecordReader.cs │ ├── RecordReader.generated.cs │ ├── RecordReader.tt │ ├── RecordsetAttribute.cs │ ├── Results.cs │ ├── Results.generated.cs │ ├── Results.tt │ ├── ResultsReader.generated.cs │ ├── ResultsReader.tt │ ├── SelfReferencingListReader.cs │ ├── SingleChildMapper.cs │ ├── SingleChildren.cs │ ├── SingleReader.cs │ ├── SingleReader.generated.cs │ └── SingleReader.tt ├── Insight.Database.Json ├── GlobalSuppressions.cs ├── Insight.Database.Json.csproj ├── JsonNetObjectSerializer.cs └── Properties │ └── AssemblyInfo.cs ├── Insight.Database.Providers.Default ├── DeriveParameters │ ├── SqlObjectName.cs │ └── SqlParameterHelper.cs ├── GlobalSuppressions.cs ├── Insight.Database.Providers.Default.csproj ├── Properties │ └── AssemblyInfo.cs ├── ReliableConnectionExtensions.cs ├── SqlConnectionStringBuilderExtensions.cs ├── SqlDataRecordAdapter.cs ├── SqlExtensions.cs └── SqlInsightDbProvider.cs ├── Insight.Database.Providers.MiniProfiler ├── GlobalSuppressions.cs ├── Insight.Database.Providers.MiniProfiler.csproj ├── MiniProfilerInsightDbProvider.cs └── Properties │ └── AssemblyInfo.cs ├── Insight.Database.Providers.MsSqlClient ├── GlobalSuppressions.cs ├── Insight.Database.Providers.MsSqlClient.csproj ├── Properties │ └── AssemblyInfo.cs ├── ReliableConnectionExtensions.cs ├── SqlConnectionStringBuilderExtensions.cs ├── SqlDataRecordAdapter.cs ├── SqlExtensions.cs └── SqlInsightDbProvider.cs ├── Insight.Database.Providers.MySql ├── GlobalSuppressions.cs ├── Insight.Database.Providers.MySql.csproj ├── MySqlInsightDbProvider.cs └── Properties │ └── AssemblyInfo.cs ├── Insight.Database.Providers.MySqlConnector ├── GlobalSuppressions.cs ├── Insight.Database.Providers.MySqlConnector.csproj ├── MySqlConnectorInsightDbProvider.cs └── Properties │ └── AssemblyInfo.cs ├── Insight.Database.Providers.OracleManaged.Core ├── GlobalSuppressions.cs ├── Insight.Database.Providers.OracleManaged.Core.csproj ├── OracleInsightDbProvider.cs └── Properties │ └── AssemblyInfo.cs ├── Insight.Database.Providers.PostgreSQL ├── GlobalSuppressions.cs ├── Insight.Database.Providers.PostgreSQL.csproj ├── NpgsqlCommandWithRecordsets.cs ├── NpgsqlConnectionWithRecordsets.cs ├── NpgsqlConnectionWithSchema.cs ├── NpgsqlExtensions.cs ├── PostgreSQLInsightDbProvider.cs └── Properties │ └── AssemblyInfo.cs ├── Insight.Database └── Insight.Database.csproj ├── Insight.Sample ├── App.config ├── Beer.cs ├── Dictionary.cs ├── Insight.Sample.csproj ├── InsightSample.sql └── Program.cs ├── Insight.Tests.Json ├── App.config ├── Insight.Tests.Json.csproj └── JsonTests.cs ├── Insight.Tests.MiniProfiler ├── App.config ├── Insight.Tests.MiniProfiler.csproj └── MiniProfilerTests.cs ├── Insight.Tests.MsSqlClient ├── App.config ├── AsyncQueryCoreTests.cs ├── BulkCopyTests.cs ├── Cases │ ├── ConnectionStateCase.cs │ ├── OutputParameters.cs │ └── Records.cs ├── Insight.Tests.MsSqlClient.csproj ├── MsSqlClientBaseTest.cs ├── SqlServerTests.cs ├── SyncQueryCoreTests.cs ├── TableValuedParametersTests.cs ├── TypeTests.cs └── XmlTests.cs ├── Insight.Tests.MySql ├── Insight.Tests.MySql.csproj ├── MySqlTests.cs └── app.config ├── Insight.Tests.MySqlConnector ├── Insight.Tests.MySqlConnector.csproj └── MySqlConnectorTests.cs ├── Insight.Tests.OracleManaged.Core ├── App.config ├── Insight.Tests.OracleManaged.Core.csproj └── OracleTests.cs ├── Insight.Tests.PostgreSQL ├── Insight.Tests.PostgreSQL.csproj └── PostgreSQLTests.cs ├── Insight.Tests.SQLite ├── Insight.Tests.SQLite.csproj ├── SQLiteTests.cs └── app.config ├── Insight.Tests ├── App.config ├── AsyncExecuteScalarTests.cs ├── AsyncExecuteTests.cs ├── AsyncInsertTests.cs ├── AsyncMiscTests.cs ├── AsyncQueryCoreTests.cs ├── AsyncQueryCoreTests.generated.cs ├── AsyncQueryCoreTests.tt ├── AsyncQueryReaderTests.cs ├── BaseTest.cs ├── BulkCopyTests.cs ├── Cases │ ├── ConnectionStateCase.cs │ ├── Issue281Test.cs │ ├── OutputParameters.cs │ └── Records.cs ├── ColumnAttributeTests.cs ├── ConnectionStringSettingsTests.cs ├── ConstructorTests.cs ├── DbReaderTests.cs ├── DynamicConnectionTest.cs ├── DynamicParameterTests.cs ├── ExpandoTests.cs ├── GenericTypes.tt ├── GroupByTests.cs ├── InsertTests.cs ├── Insight.Tests.csproj ├── InsightDbTest.sql ├── InterfaceTests.cs ├── JsonTests.cs ├── ListTests.cs ├── MappingTests.cs ├── MultipleResultsTests.cs ├── ObjectReaderTests.cs ├── OdbcTests.cs ├── OleDbTest.cs ├── OpenTests.cs ├── OptimisticTests.cs ├── OutputParameterTests.cs ├── ParameterDataTypeMapperTests.cs ├── ParentAndChildTests.cs ├── RegressionTests.cs ├── RetryStrategyTests.cs ├── SerializationTests.cs ├── SqlServerTypes │ └── readme.htm ├── StructureTests.cs ├── SubObjectTests.cs ├── SyncExecuteScalarTests.cs ├── SyncExecuteTests.cs ├── SyncInsertTests.cs ├── SyncQueryCoreTests.cs ├── SyncQueryCoreTests.generated.cs ├── SyncQueryCoreTests.tt ├── TableValuedParametersTests.cs ├── TestDataClasses.cs ├── TupleTests.cs ├── TypeTests.cs └── XmlTests.cs ├── Insight.sln ├── InsightDatabase.png ├── License.txt ├── README.md ├── SharedConfiguration.csproj ├── SharedTestConfiguration.csproj ├── build.sh └── insight-docker ├── docker-compose.yml ├── docker-env.bat └── docker-env.sh /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | end_of_line = crlf 3 | insert_final_newline = true 4 | indent_style = tab 5 | indent_size = 4 6 | trim_tailing_whitespace = true 7 | 8 | [*.yml] 9 | indent_style = space 10 | indent_size = 2 11 | trim_tailing_whitespace = true 12 | 13 | # T4 templates don't like final newlines 14 | [*.tt] 15 | insert_final_newline = false 16 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # vagrant configurations should be lf endings 2 | vagrant/* text eol=lf 3 | Vagrant/*.cnf text eol=lf 4 | Vagrant/*.conf text eol=lf -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Report an issue you're experiencing 4 | 5 | --- 6 | 7 | # Describe the bug 8 | 9 | 10 | 11 | ## Steps to reproduce 12 | 13 | 14 | 15 | ## Expected behavior 16 | 17 | 18 | 19 | - Dotnet version: [netcore2, dotnet472] 20 | - Database: [SQL Server, Oracle] 21 | - Library version: [6.2.3] 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest a new feature or enhancement for Insight.Database 4 | 5 | --- 6 | 7 | # Describe the feature 8 | 9 | 10 | 11 | ## Is this feature related to a problem, describe 12 | 13 | 14 | 15 | ## Additional context 16 | 17 | 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Support.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Support 3 | about: Ask a question or request help with Insight.Database. 4 | 5 | --- 6 | 7 | # Describe the question 8 | 9 | 10 | 11 | ## Steps to reproduce (if applicable) 12 | 13 | 14 | 15 | - Dotnet version: [netcore2, dotnet472] 16 | - Database: [SQL Server, Oracle, MySQL] 17 | - Library version: [6.2.3] 18 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | Complete as needed 4 | 5 | ## Checklist 6 | Please make sure your pull request fulfills the following requirements: 7 | 8 | - [ ] Tests for any changes have been added (for bug fixes / features). 9 | - [ ] Docs have been added / updated (for bug fixes / features). 10 | 11 | ## Type 12 | This pull request includes what type of changes? 13 | 14 | 15 | - [ ] Bug. 16 | - [ ] Feature. 17 | - [ ] Code style update (formatting, local variables). 18 | - [ ] Refactoring (no functional changes, no api changes). 19 | - [ ] Documentation content changes. 20 | - [ ] Other (please describe below). 21 | 22 | 23 | ## Breaking Changes 24 | Does this pull request introduce any breaking changes? 25 | 26 | 27 | - [ ] Yes 28 | - [ ] No 29 | 30 | ### Any other comment 31 | 32 | (n/a) 33 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 90 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 30 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | # Label to use when marking an issue as stale 10 | staleLabel: stale 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | This issue has been automatically marked as stale because it has not had 14 | recent activity. It will be closed if no further activity occurs. Thank you 15 | for your contributions. 16 | 17 | # Comment to post when closing a stale issue. Set to `false` to disable 18 | closeComment: false 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | bin 3 | obj 4 | *.user 5 | *.suo 6 | *.docstates 7 | *.nupkg 8 | *.orig 9 | *.rej 10 | *.dll 11 | StyleCop.Cache 12 | Output 13 | TestResult.xml 14 | testdb.sqlite 15 | packages 16 | 17 | # ReSharper is a .NET coding add-in 18 | _ReSharper*/ 19 | *.[Rr]e[Ss]harper 20 | 21 | .vagrant 22 | .vscode 23 | .vs 24 | -------------------------------------------------------------------------------- /Build/push.bat: -------------------------------------------------------------------------------- 1 | for %%p in (*.nupkg) do nuget push %%p -source https://www.nuget.org/api/v2/package -------------------------------------------------------------------------------- /Build/push.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | for file in ./Output/*; do 3 | dotnet nuget push $file --source https://www.nuget.org/api/v2/package -k $1 4 | done -------------------------------------------------------------------------------- /Building.md: -------------------------------------------------------------------------------- 1 | Notes for Building Insight 2 | =============================================== 3 | 4 | # Docker for Test Databases # 5 | 6 | You can run all of the supported databases through docker: 7 | 8 | cd insight-docker 9 | .\docker-env.bat # windows 10 | source ./docker-env.sh # non-windows 11 | docker-compose up 12 | 13 | It takes a while the first time, so wait until all of the databases boot up fully. 14 | 15 | NOTE: `docker-env` will set the `INSIGHT_TEST_HOST` and `INSIGHT_TEST_PASSWORD` environment variables. You'll need them 16 | for any process that is running the tests and needs to connect to the database. 17 | 18 | # Command-Line Builds # 19 | 20 | Use: ./build.sh 21 | 22 | [default] builds everything 23 | -t tests everything 24 | -p packages everything 25 | 26 | # Tagging a Version # 27 | 28 | 1. Update the version number in SharedConfiguration.csproj. 29 | 2. Use `git tag ` to mark the changeset. 30 | 3. `build package` to make the build. 31 | 4. `git push --tags` to push the tags to the server. 32 | -------------------------------------------------------------------------------- /CodeAnalysisRules.ruleset: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Insight.Database.Configuration/Insight.Database.Configuration.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Extensions to support ConnectionSettings with Insight.Database. 5 | 6 | Insight orm sql micro-orm sqlserver db2 glimpse miniprofiler mysql oracle postgres sybase sybasease sqlite sqllite 7 | 8 | 9 | 10 | LibraryPackage 11 | 12 | 13 | 14 | net8.0;netstandard2.0;net48 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Insight.Database.Configuration/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | 6 | // General Information about an assembly is controlled through the following 7 | // set of attributes. Change these attribute values to modify the information 8 | // associated with an assembly. 9 | [assembly: AssemblyCopyright("Copyright © Jon Wagner, used by Permission")] 10 | [assembly: ComVisible(false)] 11 | [assembly: CLSCompliant(true)] 12 | -------------------------------------------------------------------------------- /Insight.Database.Configuration/ReliableConnectionExtensions.cs: -------------------------------------------------------------------------------- 1 | #if !NO_CONNECTION_SETTINGS 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Configuration; 5 | using System.Data.SqlClient; 6 | using System.Linq; 7 | using System.Text; 8 | using Insight.Database; 9 | 10 | namespace Insight.Database.Reliable 11 | { 12 | /// 13 | /// Extension methods for Reliable Connections. 14 | /// 15 | public static class ReliableConnectionExtensions 16 | { 17 | #region ConnectionStringSettings Extensions 18 | /// 19 | /// Creates and returns a new SqlConnection. 20 | /// 21 | /// The ConnectionStringSettings containing the connection string. 22 | /// A closed SqlConnection. 23 | public static ReliableConnection ReliableConnection(this ConnectionStringSettings settings) 24 | { 25 | if (settings == null) 26 | { 27 | throw new ArgumentNullException("settings", "ConnectionStringSettings cannot be null"); 28 | } 29 | 30 | return new ReliableConnection(settings.Connection()); 31 | } 32 | 33 | /// 34 | /// Opens and returns a database connection. 35 | /// 36 | /// The connection string to open and return. 37 | /// The opened connection. 38 | public static ReliableConnection ReliableOpen(this ConnectionStringSettings settings) 39 | { 40 | if (settings == null) 41 | { 42 | throw new ArgumentNullException("settings", "ConnectionStringSettings cannot be null"); 43 | } 44 | 45 | ReliableConnection connection = settings.ReliableConnection(); 46 | connection.Open(); 47 | return connection; 48 | } 49 | #endregion 50 | 51 | #region Dynamic Methods 52 | /// 53 | /// Converts the connection to a connection that can be invoked dynamically to return lists of FastExpando. 54 | /// 55 | /// The connection to use. 56 | /// A DynamicConnection using the given connection. 57 | public static dynamic ReliableDynamic(this ConnectionStringSettings settings) 58 | { 59 | return settings.ReliableConnection().Dynamic(); 60 | } 61 | 62 | /// 63 | /// Converts the connection to a connection that can be invoked dynamically to return lists of type T. 64 | /// 65 | /// The connection to use. 66 | /// The type of object to return from queries. 67 | /// A DynamicConnection using the given connection. 68 | public static dynamic ReliableDynamic(this ConnectionStringSettings settings) 69 | { 70 | return settings.ReliableConnection().Dynamic(); 71 | } 72 | #endregion 73 | } 74 | } 75 | #endif 76 | -------------------------------------------------------------------------------- /Insight.Database.Core/CodeGenerator/SchemaMappingType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Insight.Database.CodeGenerator 8 | { 9 | /// 10 | /// Defines the type of a schema mapping and therefore the signature of the delegate. 11 | /// 12 | [Flags] 13 | enum SchemaMappingType 14 | { 15 | /// 16 | /// An existing object is updated. 17 | /// By itself, ExistingObject creates a method of Func<IDataReader,T,T>. 18 | /// 19 | ExistingObject = 0, 20 | 21 | /// 22 | /// A new object is created. 23 | /// By itself, NewObject creates a method of type Func<IDataReader,T>. 24 | /// 25 | NewObject = 1 << 0, 26 | 27 | /// 28 | /// A callback is used to assemble sub-objects. 29 | /// This currently cannot be used with ExistingObject. 30 | /// 31 | WithCallback = 1 << 1, 32 | 33 | /// 34 | /// A new object is created and a callback is used to assemble sub-objects. 35 | /// This creates a method of type Func<IDataReader,Action<object[]>T>. 36 | /// 37 | NewObjectWithCallback = NewObject | WithCallback, 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Insight.Database.Core/CodeGenerator/StaticFieldStorage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Reflection.Emit; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace Insight.Database.CodeGenerator 12 | { 13 | /// 14 | /// Allows storage of a static variable in a way that can be easily emitted into IL. 15 | /// 16 | /// 17 | /// We store the information in the static fields of a class because it is easy to access them 18 | /// in the IL of a DynamicMethod. 19 | /// 20 | public static class StaticFieldStorage 21 | { 22 | /// 23 | /// Stores the static objects to return later. 24 | /// 25 | static List _values = new List(); 26 | 27 | /// 28 | /// Returns a value from the static field cache. 29 | /// 30 | /// The index into the cache. 31 | /// The value from the cache. 32 | public static object GetValue(int index) 33 | { 34 | return _values[index]; 35 | } 36 | 37 | /// 38 | /// Adds a value to the cache. 39 | /// 40 | /// The value to add to the cache. 41 | /// The index into the cache. 42 | internal static int CacheValue(object value) 43 | { 44 | lock (_values) 45 | { 46 | _values.Add(value); 47 | return _values.Count - 1; 48 | } 49 | } 50 | 51 | /// 52 | /// Emits the value stored in static storage. 53 | /// 54 | /// The ILGenerator to emit to. 55 | /// The value to emit. 56 | internal static void EmitLoad(ILGenerator il, object value) 57 | { 58 | if (value == null) 59 | { 60 | il.Emit(OpCodes.Ldnull); 61 | } 62 | else 63 | { 64 | il.Emit(OpCodes.Ldc_I4, CacheValue(value)); 65 | il.Emit(OpCodes.Call, typeof(StaticFieldStorage).GetMethod("GetValue")); 66 | il.Emit(OpCodes.Castclass, value.GetType()); 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Insight.Database.Core/Compatibility.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace Insight.Database.MissingExtensions 7 | { 8 | /// 9 | /// Adds missing methods. 10 | /// 11 | public static class MissingExtensions 12 | { 13 | /// 14 | /// Determines if a string is null or all whitespace. 15 | /// 16 | /// The string to test. 17 | /// False if the string contains at least one non-whitespace character. 18 | public static bool IsNullOrWhiteSpace(this string value) 19 | { 20 | return String.IsNullOrWhiteSpace(value); 21 | } 22 | 23 | /// 24 | /// Returns the maximum value in a sequence or the default. 25 | /// 26 | /// The type of the sequence. 27 | /// The type of the value. 28 | /// The list to evaluate. 29 | /// A function to select the value. 30 | /// The maximum selected value or the default. 31 | public static T2 MaxOrDefault(this IEnumerable list, Func selector) 32 | { 33 | if (!list.Any()) 34 | return default(T2); 35 | 36 | return list.Max(selector); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Insight.Database.Core/CustomDictionary.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | AsParallel 6 | DbConnectionWrapper 7 | DefaultMappingHandler 8 | GetProperty 9 | IDictionary 10 | SetProperty 11 | SqlCommand 12 | 13 | 14 | AsParallel 15 | DbConnectionWrapper 16 | DefaultMappingHandler 17 | GetProperty 18 | IDictionary 19 | SetProperty 20 | SqlCommand 21 | 22 | 23 | 24 | 25 | Db 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Insight.Database.Core/Expando/ExpandoExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Insight.Database 7 | { 8 | /// 9 | /// Helper class to make expando objects easier to use. 10 | /// 11 | public static class ExpandoExtensions 12 | { 13 | /// 14 | /// Converts an object to a FastExpando. 15 | /// 16 | /// The type of object to convert. 17 | /// The object to convert. 18 | /// A fast expando containing the public properties of the object. 19 | public static FastExpando Expand(this T obj) 20 | { 21 | return FastExpando.FromObject(obj); 22 | } 23 | 24 | /// 25 | /// Converts an object to a FastExpando. 26 | /// 27 | /// The type of object to merge into. 28 | /// The type of object to merge. 29 | /// The object to convert. 30 | /// The other object to merge in. 31 | /// A fast expando containing the public properties of the object. 32 | public static FastExpando Expand(this T1 obj1, T2 obj2) 33 | { 34 | return FastExpando.FromObject(obj1).Expand(obj2); 35 | } 36 | 37 | /// 38 | /// Mutates a list of FastExpandos. 39 | /// 40 | /// The list to mutate. 41 | /// The mapping of input to output field names. 42 | /// The same list, but mutated. 43 | public static IEnumerable Mutate(this IEnumerable list, Dictionary map) 44 | { 45 | foreach (FastExpando obj in list) 46 | { 47 | if (obj == null) 48 | { 49 | yield return null; 50 | } 51 | else 52 | { 53 | obj.Mutate(map); 54 | yield return obj; 55 | } 56 | } 57 | } 58 | 59 | /// 60 | /// Transforms a list of FastExpandos. 61 | /// 62 | /// The list to transform. 63 | /// The mapping of input to output field names. 64 | /// A new list of transformed expandos. 65 | public static IEnumerable Transform(this IEnumerable list, Dictionary map) 66 | { 67 | foreach (FastExpando obj in list) 68 | { 69 | if (obj == null) 70 | yield return null; 71 | else 72 | yield return obj.Transform(map); 73 | } 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Insight.Database.Core/Extensions/DbReaderExtensions.tt: -------------------------------------------------------------------------------- 1 | // 2 | <#@ template debug="false" hostspecific="false" language="C#" #> 3 | <#@ assembly name="System.Core" #> 4 | <#@ import namespace="System.Linq" #> 5 | <#@ import namespace="System.Text" #> 6 | <#@ import namespace="System.Collections.Generic" #> 7 | <#@ output extension="generated.cs" #> 8 | <#@ include file="..\GenericTypes.tt" #> 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Data; 12 | using System.Diagnostics.CodeAnalysis; 13 | using System.Linq; 14 | 15 | namespace Insight.Database 16 | { 17 | /// 18 | /// Extension methods for object mapping. 19 | /// 20 | public static partial class DBReaderExtensions 21 | { 22 | <# 23 | for (var typeCount = 1; typeCount <= GenericTypeMax; typeCount++) 24 | { 25 | var typeList = GetGenericList(typeCount, GenericTypeFormat); 26 | var argList = GetGenericList(typeCount, GenericArgumentFormat); 27 | var paramList = GetGenericList(typeCount, GenericParameterFormat); 28 | #> 29 | /// 30 | /// Converts an IDataReader to an enumerable. The reader is closed after all records are read. 31 | /// 32 | <# for (var typeIndex = 1; typeIndex <= typeCount; typeIndex++) { #> 33 | /// The type of the data in the <#= ConvertToOrdinal(typeIndex) #> subobject. 34 | <# } #> 35 | /// The data reader. 36 | /// An enumerable over the return results. 37 | /// 38 | /// If you use this method and are relying on CommandBehavior.CloseConnection to close the connection, note that if all of the records are not read 39 | /// (due to an exception or otherwise), then the connection will leak until GC is run. Your code is responsible for closing the connection. 40 | /// 41 | public static IEnumerable AsEnumerable<<#= typeList #>>(this IDataReader reader) 42 | { 43 | return reader.AsEnumerable(OneToOne<<#= typeList #>>.Records); 44 | } 45 | 46 | /// 47 | /// Converts an IDataReader to a list of objects. 48 | /// 49 | <# for (var typeIndex = 1; typeIndex <= typeCount; typeIndex++) { #> 50 | /// The type of the data in the <#= ConvertToOrdinal(typeIndex) #> subobject. 51 | <# } #> 52 | /// The data reader. 53 | /// A list of objects. 54 | public static IList ToList<<#= typeList #>>(this IDataReader reader) 55 | { 56 | return reader.AsEnumerable<<#= typeList #>>().ToList(); 57 | } 58 | 59 | /// 60 | /// Converts an IDataReader to a single object. 61 | /// 62 | <# for (var typeIndex = 1; typeIndex <= typeCount; typeIndex++) { #> 63 | /// The type of the data in the <#= ConvertToOrdinal(typeIndex) #> subobject. 64 | <# } #> /// The data reader. 65 | /// A list of objects. 66 | public static T1 Single<<#= typeList #>>(this IDataReader reader) 67 | { 68 | return reader.Single(OneToOne<<#= typeList #>>.Records); 69 | } 70 | 71 | <# } #> 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Insight.Database.Core/Extensions/IAsyncEnumerable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace Insight.Database 5 | { 6 | /// 7 | /// Allows record streams to be read asynchronously. 8 | /// 9 | /// The type of object returned by the enumerator. 10 | public interface IAsyncEnumerable : IDisposable 11 | { 12 | /// 13 | /// Gets the current object in the stream. 14 | /// 15 | T Current { get; } 16 | 17 | /// 18 | /// Moves to the next record asynchronously and returns a task representing whether a record was retrieved. 19 | /// 20 | /// A task representing whether a record was retrieved. 21 | Task MoveNextAsync(); 22 | 23 | /// 24 | /// Advances the reader to the next result. 25 | /// 26 | /// A task representing whether there is another result set. 27 | Task NextResultAsync(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Insight.Database.Core/GenericTypes.tt: -------------------------------------------------------------------------------- 1 | <#+ 2 | /// 3 | /// Helper constants for T4 templates for generic types. 4 | /// 5 | 6 | private const int GenericTypeMax = 16; 7 | private const string GenericTypeFormat = "T{0}"; 8 | private const string GenericArgumentFormat = "T{0} arg{0}"; 9 | private const string GenericParameterFormat = "arg{0}"; 10 | 11 | private static readonly string[] ordinalNumbers = new[] { "first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eighth", "nineth", "tenth", "eleventh", "twelfth", "thirteenth", "fourteenth", "fifteenth", "sixteenth" }; 12 | private static readonly string[] wordNumbers = new[] { "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen" }; 13 | 14 | public static string ConvertToWord(int number) 15 | { 16 | return wordNumbers[number - 1]; 17 | } 18 | 19 | public static string ConvertToOrdinal(int number) 20 | { 21 | return ordinalNumbers[number - 1]; 22 | } 23 | 24 | private static string GetGenericList(int typeCount, string format, string separator = ", ") 25 | { 26 | return string.Join(separator, Enumerable.Range(1, typeCount).Select(n => string.Format(format, n)).ToArray()); 27 | } 28 | #> -------------------------------------------------------------------------------- /Insight.Database.Core/Helpers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Insight.Database 9 | { 10 | /// 11 | /// Internal helpers. 12 | /// 13 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed", Justification = "The reader is owned by other code.")] 14 | public static class Helpers 15 | { 16 | /// 17 | /// Represents a completed false task. 18 | /// 19 | internal static readonly Task FalseTask = Task.FromResult(false); 20 | 21 | /// 22 | /// Returns a completed task from the given result. 23 | /// 24 | /// The type of the result. 25 | /// The result. 26 | /// A completed task. 27 | internal static Task FromResult(T result) 28 | { 29 | return Task.FromResult(result); 30 | } 31 | 32 | /// 33 | /// Determines whether two strings are case-insensitive equal. 34 | /// 35 | /// The first string. 36 | /// The second string. 37 | /// True if they are equal. 38 | internal static bool IsIEqualTo(this string s1, string s2) 39 | { 40 | return String.Compare(s1, s2, StringComparison.OrdinalIgnoreCase) == 0; 41 | } 42 | 43 | /// 44 | /// Returns the only object matching the predicate, or default/null otherwise. 45 | /// 46 | /// The type of object in the enumeration. 47 | /// The enumberable to scan. 48 | /// The predicate to test. 49 | /// The only object matching the predicate, or default/null. 50 | internal static T OnlyOrDefault(this IEnumerable enumerable, Func predicate) 51 | { 52 | bool found = false; 53 | var result = default(T); 54 | 55 | foreach (var t in enumerable.Where(predicate)) 56 | { 57 | if (found) 58 | { 59 | return default(T); 60 | } 61 | else 62 | { 63 | found = true; 64 | result = t; 65 | } 66 | } 67 | 68 | return result; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Insight.Database.Core/Mapping/BindChildrenAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Insight.Database.Mapping; 6 | 7 | namespace Insight.Database 8 | { 9 | /// 10 | /// Specifes when the fields of child objects can be bound on a class or on the parameters of an interface method. 11 | /// 12 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1019:DefineAccessorsForAttributeArguments")] 13 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] 14 | public sealed class BindChildrenAttribute : Attribute 15 | { 16 | /// 17 | /// Initializes a new instance of the BindChildrenAttribute class. 18 | /// 19 | public BindChildrenAttribute() 20 | { 21 | For = BindChildrenFor.All; 22 | } 23 | 24 | /// 25 | /// Initializes a new instance of the BindChildrenAttribute class. 26 | /// 27 | /// Specifies the times when child fields can be bound. 28 | public BindChildrenAttribute(BindChildrenFor bindFor) 29 | { 30 | For = bindFor; 31 | } 32 | 33 | /// 34 | /// Gets the valid times when child fields can be bound. 35 | /// 36 | public BindChildrenFor For { get; private set; } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Insight.Database.Core/Mapping/BindChildrenFor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Insight.Database 8 | { 9 | /// 10 | /// Specifies under which circumstances database objects should be bound to child objects. 11 | /// 12 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1714:FlagsEnumsShouldHavePluralNames")] 13 | [Flags] 14 | public enum BindChildrenFor 15 | { 16 | /// 17 | /// Do not bind child fields. 18 | /// 19 | None = 0, 20 | 21 | /// 22 | /// Bind child fields when mapping input and output parameters. 23 | /// 24 | Parameters = 1, 25 | 26 | /// 27 | /// Bind child fields when mapping TVPs and Insert/Update/Upsert results. 28 | /// 29 | Tables = 2, 30 | 31 | /// 32 | /// Always bind child fields. 33 | /// 34 | All = Parameters | Tables 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Insight.Database.Core/Mapping/ColumnAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection.Emit; 5 | using System.Text; 6 | 7 | namespace Insight.Database 8 | { 9 | /// 10 | /// Defines an override to the standard mapping of database fields to object fields. 11 | /// 12 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1019:DefineAccessorsForAttributeArguments")] 13 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)] 14 | public sealed class ColumnAttribute : Attribute 15 | { 16 | #region Constructors 17 | /// 18 | /// Initializes a new instance of the ColumnAttribute class. 19 | /// 20 | public ColumnAttribute() 21 | { 22 | } 23 | 24 | /// 25 | /// Initializes a new instance of the ColumnAttribute class. 26 | /// 27 | /// The name of the column to map this field to. 28 | public ColumnAttribute(string columnName) 29 | { 30 | ColumnName = columnName; 31 | } 32 | #endregion 33 | 34 | #region Properties 35 | /// 36 | /// Gets or sets the name of the column to map this field to. 37 | /// 38 | public string ColumnName { get; set; } 39 | 40 | /// 41 | /// Gets or sets the serialization mode for the column. 42 | /// 43 | public SerializationMode SerializationMode { get; set; } 44 | 45 | /// 46 | /// Gets or sets the type of serializer to use for the column. 47 | /// 48 | public Type Serializer { get; set; } 49 | #endregion 50 | 51 | /// 52 | /// Returns a custom attribute builder for the attribute. 53 | /// 54 | /// The CustomAttributeBuilder. 55 | internal CustomAttributeBuilder GetCustomAttributeBuilder() 56 | { 57 | var properties = new[] 58 | { 59 | typeof(ColumnAttribute).GetProperty("ColumnName"), 60 | typeof(ColumnAttribute).GetProperty("SerializationMode"), 61 | typeof(ColumnAttribute).GetProperty("Serializer") 62 | }; 63 | 64 | var values = new object[] 65 | { 66 | ColumnName, 67 | SerializationMode, 68 | Serializer 69 | }; 70 | 71 | return new CustomAttributeBuilder( 72 | typeof(ColumnAttribute).GetConstructor(Type.EmptyTypes), 73 | Parameters.EmptyArray, 74 | properties, 75 | values); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Insight.Database.Core/Mapping/DualMappingCollection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Insight.Database.Mapping 8 | { 9 | /// 10 | /// Allows for configuration of both the Parameters and Tables mappings at the same time. 11 | /// 12 | class DualMappingCollection : MappingCollection 13 | { 14 | /// 15 | /// The parameters submapping. 16 | /// 17 | private MappingCollection _parameters; 18 | 19 | /// 20 | /// The tables submapping. 21 | /// 22 | private MappingCollection _tables; 23 | 24 | /// 25 | /// Initializes a new instance of the DualMappingCollection class 26 | /// 27 | /// The paremeters submapping. 28 | /// The tables submapping. 29 | public DualMappingCollection(MappingCollection parameters, MappingCollection tables) : base(BindChildrenFor.All) 30 | { 31 | _parameters = parameters; 32 | _tables = tables; 33 | } 34 | 35 | /// 36 | public override MappingCollection EnableChildBinding() 37 | { 38 | _parameters.EnableChildBinding(); 39 | _tables.EnableChildBinding(); 40 | return this; 41 | } 42 | 43 | /// 44 | public override MappingCollection DisableChildBinding() 45 | { 46 | _parameters.DisableChildBinding(); 47 | _tables.DisableChildBinding(); 48 | return this; 49 | } 50 | 51 | /// 52 | public override MappingCollection ResetChildBinding() 53 | { 54 | _parameters.ResetChildBinding(); 55 | _tables.ResetChildBinding(); 56 | return this; 57 | } 58 | 59 | /// 60 | public override MappingCollection AddTransform(IMappingTransform transform) 61 | { 62 | _parameters.AddTransform(transform); 63 | _tables.AddTransform(transform); 64 | return this; 65 | } 66 | 67 | /// 68 | public override MappingCollection ResetTransforms() 69 | { 70 | _parameters.ResetTransforms(); 71 | _tables.ResetTransforms(); 72 | return this; 73 | } 74 | 75 | /// 76 | public override MappingCollection AddMapper(IDualMapper mapper) 77 | { 78 | _parameters.AddMapper(mapper); 79 | _tables.AddMapper(mapper); 80 | return this; 81 | } 82 | 83 | /// 84 | public override MappingCollection ResetMappers() 85 | { 86 | _parameters.ResetMappers(); 87 | _tables.ResetMappers(); 88 | return this; 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /Insight.Database.Core/Mapping/FieldMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Insight.Database.CodeGenerator; 7 | 8 | namespace Insight.Database.Mapping 9 | { 10 | /// 11 | /// Represents the mapping from a database object to a class member. 12 | /// 13 | class FieldMapping 14 | { 15 | #region Constructors 16 | /// 17 | /// Initializes a new instance of the FieldMapping class. 18 | /// 19 | /// The path to the member. 20 | /// The member that is bound. 21 | /// The serializer for the mapping. 22 | public FieldMapping(string pathToMember, ClassPropInfo member, IDbObjectSerializer serializer) 23 | { 24 | PathToMember = pathToMember; 25 | Member = member; 26 | Serializer = serializer; 27 | Prefix = ClassPropInfo.GetMemberPrefix(pathToMember); 28 | IsDeep = (Prefix != null); 29 | } 30 | #endregion 31 | 32 | #region Properties 33 | /// 34 | /// Gets the path to get to the member, in dotted notation (a.b.c.d). 35 | /// 36 | public string PathToMember { get; private set; } 37 | 38 | /// 39 | /// Gets the member this is bound to. 40 | /// 41 | public ClassPropInfo Member { get; private set; } 42 | 43 | /// 44 | /// Gets the serializer for this mapping. 45 | /// 46 | public IDbObjectSerializer Serializer { get; private set; } 47 | 48 | /// 49 | /// Gets a value indicating whether this is a mapping into a subobject. 50 | /// 51 | public bool IsDeep { get; private set; } 52 | 53 | /// 54 | /// Gets the prefix part of the PathToMember. 55 | /// 56 | public string Prefix { get; private set; } 57 | #endregion 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Insight.Database.Core/Mapping/IColumnMapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Insight.Database.Mapping 9 | { 10 | /// 11 | /// Maps a column to a field. 12 | /// 13 | public interface IColumnMapper : IMapper 14 | { 15 | /// 16 | /// Returns the name of the field on the given type that maps to the specified parameter. 17 | /// 18 | /// The type to test. 19 | /// The reader being parsed. 20 | /// The index of the column being mapped. 21 | /// The name of the field on type, or null to allow other mappers to handle the parameter. 22 | /// To prevent other mappers from handling a parameter, return a non-null value that does not map to a field on the type. 23 | string MapColumn(Type type, IDataReader reader, int column); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Insight.Database.Core/Mapping/IDualMapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Insight.Database.Mapping 8 | { 9 | /// 10 | /// Performs both parameter mapping and column mapping. 11 | /// 12 | public interface IDualMapper : IParameterMapper, IColumnMapper 13 | { 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Insight.Database.Core/Mapping/IMapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Insight.Database.Mapping 8 | { 9 | /// 10 | /// Performs a mapping from a database object to a class field. 11 | /// 12 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1040:AvoidEmptyInterfaces")] 13 | public interface IMapper 14 | { 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Insight.Database.Core/Mapping/IMappingTransform.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Insight.Database.Mapping 8 | { 9 | /// 10 | /// Transforms database names (e.g. parameters, columns), into object names. 11 | /// 12 | public interface IMappingTransform 13 | { 14 | /// 15 | /// Transforms the database name (parameter or column) prior to attempting to map the field. 16 | /// 17 | /// The type being bound. 18 | /// The name from the database. 19 | /// The database name, transformed. For example, by removing underscores. 20 | string TransformDatabaseName(Type type, string databaseName); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Insight.Database.Core/Mapping/IParameterDataTypeMapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data; 3 | using Insight.Database.Mapping; 4 | 5 | namespace Insight.Database.Mapping 6 | { 7 | /// 8 | /// Maps a DbType to a field, overriding. 9 | /// 10 | public interface IParameterDataTypeMapper : IMapper 11 | { 12 | /// 13 | /// Returns a DbType of the field on the given type that maps to the specified parameter. 14 | /// 15 | /// The type to test. 16 | /// The command being executed. 17 | /// The parameter being mapped. 18 | /// The best guess of which data type the parameter should be. 19 | /// The DbType to map the parameter to. Return the best guess parameter if unhandled. 20 | DbType MapParameterType(Type type, IDbCommand command, IDataParameter parameter, DbType dbType); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Insight.Database.Core/Mapping/IParameterMapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Insight.Database.Mapping 9 | { 10 | /// 11 | /// Maps a parameter to a field. 12 | /// 13 | public interface IParameterMapper : IMapper 14 | { 15 | /// 16 | /// Returns the name of the field on the given type that maps to the specified parameter. 17 | /// 18 | /// The type to test. 19 | /// The command being executed. 20 | /// The parameter being mapped. 21 | /// The name of the field on type, or null to allow other mappers to handle the parameter. 22 | /// To prevent other mappers from handling a parameter, return a non-null value that does not map to a field on the type. 23 | string MapParameter(Type type, IDbCommand command, IDataParameter parameter); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Insight.Database.Core/MergeOutputAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Insight.Database 8 | { 9 | /// 10 | /// Marks a method as an Insert/Update/Upsert method so Insight will merge the outputs onto the input objects. 11 | /// 12 | /// 13 | /// Normally when Insight auto-implements an interface, it only merges the results of methods whose names start with Insert or Update. 14 | /// If you want a method of a different name to automatically merge the results, add the MergeOutput attribute to the method. 15 | /// 16 | [AttributeUsage(AttributeTargets.Method)] 17 | public sealed class MergeOutputAttribute : Attribute 18 | { 19 | /// 20 | /// Initializes a new instance of the MergeOutputAttribute class. 21 | /// 22 | public MergeOutputAttribute() 23 | { 24 | MergeOutputs = true; 25 | } 26 | 27 | /// 28 | /// Initializes a new instance of the MergeOutputAttribute class. 29 | /// 30 | /// True to merge the outputs, false to skip merging. 31 | /// Use MergeOutput(false) to disable merging for methods named InsertXXX, UpdateXXX, or UpsertXXX. 32 | public MergeOutputAttribute(bool mergeOutputs = true) 33 | { 34 | MergeOutputs = mergeOutputs; 35 | } 36 | 37 | /// 38 | /// Gets a value indicating whether the outputs will be merged. 39 | /// 40 | public bool MergeOutputs { get; private set; } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Insight.Database.Core/Optimistic/OptimisticConcurrencyException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.Serialization; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Insight.Database 9 | { 10 | /// 11 | /// Indicates that a database call failed due to an optimistic concurrency issue. 12 | /// 13 | public class OptimisticConcurrencyException : Exception 14 | { 15 | /// 16 | /// Initializes a new instance of the OptimisticConcurrencyException class. 17 | /// 18 | public OptimisticConcurrencyException() 19 | { 20 | } 21 | 22 | /// 23 | /// Initializes a new instance of the OptimisticConcurrencyException class. 24 | /// 25 | /// The exception causing the issue. 26 | public OptimisticConcurrencyException(Exception innerException) : base("One or more records were changed.", innerException) 27 | { 28 | } 29 | 30 | /// 31 | /// Initializes a new instance of the OptimisticConcurrencyException class. 32 | /// 33 | /// The message for the exception. 34 | /// The exception causing the issue. 35 | public OptimisticConcurrencyException(string message, Exception innerException) : base(message, innerException) 36 | { 37 | } 38 | 39 | /// 40 | /// Initializes a new instance of the OptimisticConcurrencyException class. 41 | /// 42 | /// The message for the exception. 43 | public OptimisticConcurrencyException(string message) : base(message) 44 | { 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Insight.Database.Core/Optimistic/OptimisticConnection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data.Common; 4 | using System.Diagnostics.CodeAnalysis; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Insight.Database 10 | { 11 | /// 12 | /// Wraps a DbConnection and translates OptimisticConcurrencyExceptions. 13 | /// 14 | [System.ComponentModel.DesignerCategory("Code")] 15 | public class OptimisticConnection : DbConnectionWrapper 16 | { 17 | /// 18 | /// Initializes a new instance of the OptimisticConnection class. 19 | /// 20 | /// The inner connection. 21 | public OptimisticConnection(DbConnection innerConnection) 22 | : base(innerConnection) 23 | { 24 | } 25 | 26 | /// 27 | /// Returns true if the exception is an optimistic concurrency exception. 28 | /// This method may be overridden. 29 | /// 30 | /// The exception to test. 31 | /// Whether the exception is a concurrency exception. 32 | public virtual bool IsConcurrencyException(Exception exception) 33 | { 34 | if (!(exception is DbException)) 35 | return false; 36 | 37 | return exception.Message.Contains("CONCURRENCY CHECK"); 38 | } 39 | 40 | /// 41 | protected override DbCommand CreateDbCommand() 42 | { 43 | return new OptimisticCommand(this, InnerConnection.CreateCommand()); 44 | } 45 | } 46 | 47 | /// 48 | /// Wraps a DbConnection and translates OptimisticConcurrencyExceptions. 49 | /// 50 | /// The type of connection to create. 51 | [SuppressMessage("Microsoft.StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "These are related generic classes.")] 52 | public class OptimisticConnection : OptimisticConnection where TConnection : DbConnection, new() 53 | { 54 | /// 55 | /// Initializes a new instance of the OptimisticConnection class. 56 | /// 57 | public OptimisticConnection() : base(new TConnection()) 58 | { 59 | } 60 | 61 | /// 62 | /// Initializes a new instance of the OptimisticConnection class. 63 | /// 64 | /// The connection string to the database. 65 | public OptimisticConnection(string connectionString) 66 | : base(new TConnection()) 67 | { 68 | InnerConnection.ConnectionString = connectionString; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Insight.Database.Core/Parameters.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace Insight.Database 8 | { 9 | /// 10 | /// Convenience class for empty parameter list. 11 | /// 12 | public static class Parameters 13 | { 14 | /// 15 | /// An empty parameter. 16 | /// 17 | public static readonly object Empty = new object(); 18 | 19 | /// 20 | /// An empty parameter array. 21 | /// 22 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2105:ArrayFieldsShouldNotBeReadOnly", Justification = "The array is immutable and the object is immutable")] 23 | public static readonly object[] EmptyArray = new object[0]; 24 | 25 | /// 26 | /// An empty list. 27 | /// 28 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes", Justification = "The array is immutable and the object is immutable")] 29 | public static readonly IEnumerable EmptyList = new object[0]; 30 | 31 | /// 32 | /// Returns an empty list of a given type. 33 | /// 34 | /// The type contained in the list. 35 | /// An empty enumerator of the given type. 36 | public static IEnumerable EmptyListOf() 37 | { 38 | yield break; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Insight.Database.Core/PlatformCompatibility/ApplicationHelpers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Text; 7 | 8 | #if NETSTANDARD1_5 9 | using System.Runtime.Loader; 10 | #endif 11 | 12 | namespace Insight.Database 13 | { 14 | /// 15 | /// Compatibility methods for Assembly loading. 16 | /// 17 | class ApplicationHelpers 18 | { 19 | /// 20 | /// Returns a list of paths to search for assemblies. 21 | /// 22 | /// A list of patahs. 23 | internal static List GetAssemblySearchPaths() 24 | { 25 | var paths = new List(); 26 | 27 | #if NETSTANDARD1_5 28 | paths.Add(AppContext.BaseDirectory); 29 | #else 30 | string relativeSearchPath = System.AppDomain.CurrentDomain.RelativeSearchPath ?? String.Empty; 31 | paths.AddRange(relativeSearchPath.Split(';').Select(p => Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, p))); 32 | #endif 33 | return paths; 34 | } 35 | 36 | /// 37 | /// Loads an assembly from a file path. 38 | /// 39 | /// The path to the assembly. 40 | /// The loaded assembly. 41 | internal static Assembly LoadAssembly(string assemblyFilePath) 42 | { 43 | #if NETSTANDARD1_5 44 | Assembly assembly = new AssemblyLoader().LoadFromAssemblyPath(assemblyFilePath); 45 | #else 46 | Assembly assembly = Assembly.LoadFrom(assemblyFilePath); 47 | #endif 48 | return assembly; 49 | } 50 | 51 | #if NETSTANDARD1_5 52 | class AssemblyLoader : AssemblyLoadContext 53 | { 54 | protected override Assembly Load(AssemblyName assemblyName) 55 | { 56 | return Assembly.Load(assemblyName); 57 | } 58 | } 59 | #endif 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Insight.Database.Core/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.235 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | using System; 12 | using System.Reflection; 13 | using System.Runtime.CompilerServices; 14 | using System.Runtime.InteropServices; 15 | using System.Diagnostics.CodeAnalysis; 16 | 17 | [assembly: AssemblyCopyright("Copyright © Jon Wagner, used by permission")] 18 | [assembly: ComVisible(false)] 19 | [assembly: CLSCompliant(true)] 20 | -------------------------------------------------------------------------------- /Insight.Database.Core/ReflectionCompatibility.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection.Emit; 4 | using System.Text; 5 | 6 | namespace Insight.Database 7 | { 8 | #if NO_CLASSIC_REFLECTION 9 | /// 10 | /// Compatibility methods for reflection methods that are not implemented in .NET standard. 11 | /// 12 | static class ReflectionCompatibility 13 | { 14 | /// 15 | /// Creates a type from a TypeBuilder. 16 | /// 17 | /// The TypeBuilder. 18 | /// The created type. 19 | public static Type CreateType(this TypeBuilder builder) 20 | { 21 | return builder.CreateTypeInfo().AsType(); 22 | } 23 | } 24 | #endif 25 | } 26 | -------------------------------------------------------------------------------- /Insight.Database.Core/Reliable/IRetryStrategy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Insight.Database.Reliable 9 | { 10 | /// 11 | /// Defines a strategy for handling retries for transient exceptions in database connections such as SQL Azure. 12 | /// 13 | public interface IRetryStrategy 14 | { 15 | /// 16 | /// Executes a function and retries the action if a transient error is detected. 17 | /// 18 | /// The type of the result of the function. 19 | /// The IDbCommand that is expected to be executed within the function, 20 | /// or null if the operation is being performed directly on a connection. 21 | /// The function to execute. 22 | /// The result of the function. 23 | TResult ExecuteWithRetry(IDbCommand commandContext, Func func); 24 | 25 | /// 26 | /// Asynchronously executes a function and retries the action if a transient error is detected. 27 | /// 28 | /// The type of the result of the function. 29 | /// The IDbCommand that is expected to be executed within the function, 30 | /// or null if the operation is being performed directly on a connection. 31 | /// The function to execute. 32 | /// The result of the function. 33 | Task ExecuteWithRetryAsync(IDbCommand commandContext, Func> func); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Insight.Database.Core/Reliable/ReliableConnectionInsightDbProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Data.Common; 5 | using Insight.Database.Providers; 6 | 7 | namespace Insight.Database.Reliable 8 | { 9 | /// 10 | /// Implements the Insight provider for Reliable connections. 11 | /// 12 | class ReliableConnectionInsightDbProvider : DbConnectionWrapperInsightDbProvider 13 | { 14 | /// 15 | /// The list of types supported by this provider. 16 | /// 17 | private static Type[] _supportedTypes = new Type[] 18 | { 19 | typeof(ReliableConnection), typeof(ReliableCommand) 20 | }; 21 | 22 | /// 23 | /// Gets the types of objects that this provider supports. 24 | /// Include connectionstrings, connections, commands, and readers. 25 | /// 26 | public override IEnumerable SupportedTypes 27 | { 28 | get 29 | { 30 | return _supportedTypes; 31 | } 32 | } 33 | 34 | /// 35 | public override IDbConnection CloneDbConnection(IDbConnection connection) 36 | { 37 | if (connection == null) throw new ArgumentNullException("connection"); 38 | 39 | // clone the inner connection 40 | var reliable = (ReliableConnection)connection; 41 | var innerConnection = GetInnerConnection(connection); 42 | var innerProvider = InsightDbProvider.For(innerConnection); 43 | var clonedInnerConnection = innerProvider.CloneDbConnection(innerConnection); 44 | 45 | return new ReliableConnection((DbConnection)clonedInnerConnection, reliable.RetryStrategy); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Insight.Database.Core/Reliable/RetryEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace Insight.Database.Reliable 8 | { 9 | /// 10 | /// Represents an event that occurs when a database connection is about to be retried. 11 | /// 12 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable", Justification = "The command is owned by other code.")] 13 | public sealed class RetryEventArgs : EventArgs 14 | { 15 | /// 16 | /// Gets or sets the IDbCommand that is executing. 17 | /// 18 | public IDbCommand CommandContext { get; set; } 19 | 20 | /// 21 | /// Gets or sets the exception that was caught and retried. 22 | /// 23 | public Exception Exception { get; set; } 24 | 25 | /// 26 | /// Gets or sets the number of the attempt just completed. Zero is the first attempt. 27 | /// 28 | public int Attempt { get; set; } 29 | 30 | /// 31 | /// Gets or sets a value indicating whether the RetryStrategy should cancel the retry operation. 32 | /// 33 | public bool Cancel { get; set; } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Insight.Database.Core/Serialization/DbObjectSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Insight.Database.CodeGenerator; 8 | using Insight.Database.Providers; 9 | 10 | namespace Insight.Database 11 | { 12 | /// 13 | /// Converts objects to/from a serialized form (usually string, but binary would work too). 14 | /// 15 | public abstract class DbObjectSerializer : IDbObjectSerializer 16 | { 17 | #region IDbObjectSerializer Methods 18 | /// 19 | public virtual bool CanSerialize(Type type, DbType dbType) 20 | { 21 | if (!TypeHelper.IsDbTypeAString(dbType)) 22 | return false; 23 | 24 | return true; 25 | } 26 | 27 | /// 28 | public virtual bool CanDeserialize(Type sourceType, Type targetType) 29 | { 30 | return sourceType == typeof(string) && !TypeHelper.IsAtomicType(targetType) && targetType != typeof(object); 31 | } 32 | 33 | /// 34 | public virtual DbType GetSerializedDbType(Type type, DbType dbType) 35 | { 36 | return DbType.String; 37 | } 38 | 39 | /// 40 | public abstract object SerializeObject(Type type, object value); 41 | 42 | /// 43 | public abstract object DeserializeObject(Type type, object encoded); 44 | #endregion 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Insight.Database.Core/Serialization/IDbObjectSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Insight.Database 9 | { 10 | /// 11 | /// Serializes objects in the database. 12 | /// 13 | public interface IDbObjectSerializer 14 | { 15 | /// 16 | /// Returns true if the serializer can convert the type of object to serialized form. 17 | /// 18 | /// The type of object. 19 | /// The type that the database expects. 20 | /// True if the serializer can convert the type of object to serialized form. 21 | bool CanSerialize(Type type, DbType dbType); 22 | 23 | /// 24 | /// Returns true if the serializer can convert the type of object from serialized form. 25 | /// 26 | /// The type of object in the database. 27 | /// The type of the object in the target object. 28 | /// True if the serializer can convert the type of object from serialized form. 29 | bool CanDeserialize(Type sourceType, Type targetType); 30 | 31 | /// 32 | /// Returns the DbType used to serialize the given type in the database. 33 | /// Usually DbType.String, but could also be binary. 34 | /// 35 | /// The type of object. 36 | /// The type that the database expects. 37 | /// The DbType used to store the serialized representation. 38 | DbType GetSerializedDbType(Type type, DbType dbType); 39 | 40 | /// 41 | /// Serializes an object, using the type as the root type. 42 | /// 43 | /// The root type of object. 44 | /// The object to serialize. 45 | /// The serialized form of the object. 46 | object SerializeObject(Type type, object value); 47 | 48 | /// 49 | /// Deserializes an object, using the type as the root type. 50 | /// 51 | /// The root type of object. 52 | /// The object to serialize. 53 | /// The object extracted from the serialized form. 54 | object DeserializeObject(Type type, object encoded); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Insight.Database.Core/Serialization/IDbSerializationRule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Insight.Database.CodeGenerator; 7 | 8 | namespace Insight.Database 9 | { 10 | /// 11 | /// Defines a serialization rule. 12 | /// 13 | public interface IDbSerializationRule 14 | { 15 | /// 16 | /// Given a property, select the proper serializer. 17 | /// 18 | /// The type of the record being serialized. 19 | /// The type of the member being serialized. 20 | /// The name of the member being serialized. 21 | /// The serializer for the object. 22 | IDbObjectSerializer GetSerializer(Type recordType, Type memberType, string memberName); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Insight.Database.Core/Serialization/JsonObjectSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Runtime.Serialization.Json; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace Insight.Database 11 | { 12 | /// 13 | /// Serializes objects to JSON using the DataContractJsonSerializer or an overridden serializer (usually Newtonsoft.JSON). 14 | /// 15 | public class JsonObjectSerializer : DbObjectSerializer 16 | { 17 | /// 18 | /// The singleton Serializer. 19 | /// 20 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] 21 | public static readonly JsonObjectSerializer Serializer = new JsonObjectSerializer(); 22 | 23 | /// 24 | /// Gets or sets a JSON Serializer to replace the DataContractJsonSerializer. 25 | /// 26 | public static DbObjectSerializer OverrideSerializer { get; set; } 27 | 28 | /// 29 | public override bool CanSerialize(Type type, DbType dbType) 30 | { 31 | return base.CanSerialize(type, dbType) || dbType == DbType.Object; 32 | } 33 | 34 | /// 35 | public override object SerializeObject(Type type, object value) 36 | { 37 | if (OverrideSerializer != null) 38 | return OverrideSerializer.SerializeObject(type, value); 39 | 40 | if (value == null) 41 | return null; 42 | 43 | // serialize the parameters 44 | using (MemoryStream stream = new MemoryStream()) 45 | { 46 | new DataContractJsonSerializer(type).WriteObject(stream, value); 47 | 48 | return Encoding.UTF8.GetString(stream.GetBuffer(), 0, (int)stream.Length); 49 | } 50 | } 51 | 52 | /// 53 | public override object DeserializeObject(Type type, object encoded) 54 | { 55 | if (OverrideSerializer != null) 56 | return OverrideSerializer.DeserializeObject(type, encoded); 57 | 58 | DataContractJsonSerializer serializer = new DataContractJsonSerializer(type); 59 | 60 | using (var stream = new MemoryStream(Encoding.UTF8.GetBytes((string)encoded))) 61 | { 62 | return serializer.ReadObject(stream); 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Insight.Database.Core/Serialization/SerializationMode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Insight.Database 8 | { 9 | /// 10 | /// Specifies the format to serialize objects in the database. 11 | /// 12 | public enum SerializationMode 13 | { 14 | /// 15 | /// Use the default serializer. 16 | /// 17 | Default, 18 | 19 | /// 20 | /// Serialize the object as XML, using the DataContractSerializer. 21 | /// 22 | Xml, 23 | 24 | /// 25 | /// Serialize the object as JSON. 26 | /// 27 | Json, 28 | 29 | /// 30 | /// Serialize the object by calling the ToString method. 31 | /// 32 | ToString, 33 | 34 | /// 35 | /// Use a custom serializer for the column. 36 | /// 37 | Custom 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Insight.Database.Core/Serialization/ToStringObjectSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Data; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using Insight.Database.CodeGenerator; 9 | using Insight.Database.Providers; 10 | 11 | namespace Insight.Database 12 | { 13 | /// 14 | /// Serializes objects to strings. Does not deserialize. 15 | /// 16 | class ToStringObjectSerializer : DbObjectSerializer 17 | { 18 | /// 19 | /// The singleton Serializer. 20 | /// 21 | public static readonly ToStringObjectSerializer Serializer = new ToStringObjectSerializer(); 22 | 23 | /// 24 | public override bool CanSerialize(Type type, DbType dbType) 25 | { 26 | if (type == null) throw new ArgumentNullException("type"); 27 | 28 | // no point in converting convertibles or enumerables 29 | if (type.GetInterfaces().Contains(typeof(IConvertible)) || type == typeof(object)) 30 | return false; 31 | if (typeof(IEnumerable).IsAssignableFrom(type)) 32 | return false; 33 | if (type == TypeHelper.LinqBinaryType) 34 | return false; 35 | 36 | // never try to convert an atomic type 37 | if (TypeHelper.IsAtomicType(type)) 38 | return false; 39 | 40 | if (typeof(IEnumerable).IsAssignableFrom(type)) 41 | return false; 42 | 43 | if (dbType == DbType.Object) 44 | return false; 45 | 46 | return true; 47 | } 48 | 49 | /// 50 | public override bool CanDeserialize(Type sourceType, Type targetType) 51 | { 52 | return false; 53 | } 54 | 55 | /// 56 | public override DbType GetSerializedDbType(Type type, DbType dbType) 57 | { 58 | return DbType.String; 59 | } 60 | 61 | /// 62 | public override object SerializeObject(Type type, object o) 63 | { 64 | if (o == null) throw new ArgumentNullException("o"); 65 | 66 | return o.ToString(); 67 | } 68 | 69 | /// 70 | public override object DeserializeObject(Type type, object o) 71 | { 72 | return o; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Insight.Database.Core/Serialization/XmlObjectSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Globalization; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Runtime.Serialization; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using System.Xml; 11 | using Insight.Database.CodeGenerator; 12 | 13 | namespace Insight.Database 14 | { 15 | /// 16 | /// Serializes objects to XML using the DataContractSerializer. 17 | /// 18 | public class XmlObjectSerializer : DbObjectSerializer 19 | { 20 | /// 21 | /// The singleton Serializer. 22 | /// 23 | public static readonly XmlObjectSerializer Serializer = new XmlObjectSerializer(); 24 | 25 | /// 26 | public override bool CanSerialize(Type type, DbType dbType) 27 | { 28 | return TypeHelper.IsDbTypeAString(dbType) || dbType == DbType.Xml; 29 | } 30 | 31 | /// 32 | public override DbType GetSerializedDbType(Type type, DbType dbType) 33 | { 34 | return DbType.Xml; 35 | } 36 | 37 | /// 38 | public override object SerializeObject(Type type, object value) 39 | { 40 | if (value == null) 41 | return null; 42 | 43 | if (type == null) 44 | type = value.GetType(); 45 | 46 | // don't double-encode strings. assume the string is xml. 47 | if (type == typeof(string)) 48 | return (string)value; 49 | 50 | // serialize the parameters 51 | StringWriter sw = new StringWriter(CultureInfo.InvariantCulture); 52 | StringWriter disposable = sw; 53 | try 54 | { 55 | XmlWriterSettings settings = new XmlWriterSettings(); 56 | settings.OmitXmlDeclaration = true; 57 | using (XmlWriter xw = XmlWriter.Create(sw, settings)) 58 | { 59 | disposable = null; 60 | new DataContractSerializer(type).WriteObject(xw, value); 61 | } 62 | 63 | return sw.ToString(); 64 | } 65 | finally 66 | { 67 | if (disposable != null) 68 | disposable.Dispose(); 69 | } 70 | } 71 | 72 | /// 73 | public override object DeserializeObject(Type type, object encoded) 74 | { 75 | DataContractSerializer serializer = new DataContractSerializer(type); 76 | 77 | StringReader reader = new StringReader((string)encoded); 78 | try 79 | { 80 | using (XmlReader xr = XmlReader.Create(reader)) 81 | { 82 | reader = null; 83 | return serializer.ReadObject(xr); 84 | } 85 | } 86 | finally 87 | { 88 | if (reader != null) 89 | reader.Dispose(); 90 | } 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /Insight.Database.Core/SqlAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Insight.Database 9 | { 10 | /// 11 | /// Specifies that Insight should generate the specified SQL for the interface method. 12 | /// 13 | [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Interface)] 14 | public sealed class SqlAttribute : Attribute 15 | { 16 | /// 17 | /// Initializes a new instance of the SqlAttribute class. 18 | /// 19 | public SqlAttribute() 20 | { 21 | } 22 | 23 | /// 24 | /// Initializes a new instance of the SqlAttribute class. 25 | /// 26 | /// The SQL to use for the interface method. 27 | public SqlAttribute(string sql) 28 | { 29 | Sql = sql; 30 | } 31 | 32 | /// 33 | /// Initializes a new instance of the SqlAttribute class. 34 | /// 35 | /// The SQL to use for the interface method. 36 | /// The CommandType to use to execute the query. 37 | public SqlAttribute(string sql, CommandType commandType) 38 | { 39 | Sql = sql; 40 | CommandType = commandType; 41 | } 42 | 43 | /// 44 | /// Gets or sets the default SQL Schema to use when calling stored procedures. 45 | /// 46 | public string Schema { get; set; } 47 | 48 | /// 49 | /// Gets or sets the SQL to use for the interface method. 50 | /// 51 | public string Sql { get; set; } 52 | 53 | /// 54 | /// Gets or sets the CommandType to use for the interface method. 55 | /// 56 | public CommandType? CommandType { get; set; } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Insight.Database.Core/SqlConstructorAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Insight.Database 8 | { 9 | /// 10 | /// Marks a constructor as the preferred constructor to use when deserializing objects from the database. 11 | /// 12 | [AttributeUsage(AttributeTargets.Constructor)] 13 | public sealed class SqlConstructorAttribute : Attribute 14 | { 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Insight.Database.Core/Structure/ChildRecordReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | namespace Insight.Database.Structure 10 | { 11 | /// 12 | /// Reads child records and groups them by ID. 13 | /// 14 | /// The type of object to read as the record. 15 | /// The type of the key of the record. 16 | /// The type of the data of the record. 17 | class ChildRecordReader : IChildRecordReader 18 | { 19 | /// 20 | /// The reader to get the whole record. 21 | /// 22 | IRecordReader _recordReader; 23 | 24 | /// 25 | /// The function to group the results. 26 | /// 27 | Func, IEnumerable>> _grouping; 28 | 29 | /// 30 | /// Initializes a new instance of the ChildRecordReader class. 31 | /// 32 | /// The reader to get the whole record. 33 | /// The function to group the results. 34 | public ChildRecordReader(IRecordReader recordReader, Func, IEnumerable>> grouping) 35 | { 36 | _recordReader = recordReader; 37 | _grouping = grouping; 38 | } 39 | 40 | /// 41 | IEnumerable> IChildRecordReader.Read(IDataReader reader) 42 | { 43 | IEnumerable records = reader.ToList(_recordReader); 44 | 45 | if (_recordReader.RequiresDeduplication) 46 | records = records.Distinct(); 47 | 48 | return _grouping(records); 49 | } 50 | 51 | /// 52 | async Task>> IChildRecordReader.ReadAsync(IDataReader reader, CancellationToken cancellationToken) 53 | { 54 | IEnumerable records = await reader.ToListAsync(_recordReader, cancellationToken).ConfigureAwait(false); 55 | 56 | if (_recordReader.RequiresDeduplication) 57 | records = records.Distinct(); 58 | 59 | return _grouping(records); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Insight.Database.Core/Structure/ChildRecordsAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Insight.Database 8 | { 9 | /// 10 | /// Marks a field or property as the one that Insight should prefer to use to put lists of child records. 11 | /// 12 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] 13 | public sealed class ChildRecordsAttribute : Attribute 14 | { 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Insight.Database.Core/Structure/ColumnOverride.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Insight.Database 9 | { 10 | /// 11 | /// Represents a column mapping that overrides the default rules. 12 | /// ColumnOverrides are processed after any default rules are processed. 13 | /// 14 | [SuppressMessage("Microsoft.StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "The classes are related by implementing multiple generic signatures.")] 15 | public class ColumnOverride 16 | { 17 | #region Constructors 18 | /// 19 | /// Initializes a new instance of the ColumnOverride class. 20 | /// 21 | /// The name of the column to map. 22 | /// The name of the field to map to. 23 | public ColumnOverride(string columnName, string fieldName) : this(null, columnName, fieldName) 24 | { 25 | } 26 | 27 | /// 28 | /// Initializes a new instance of the ColumnOverride class. 29 | /// 30 | /// The type of object to perform mapping on or null to match all objects. 31 | /// The name of the column to map. 32 | /// The name of the field to map to. 33 | public ColumnOverride(Type targetType, string columnName, string fieldName) 34 | { 35 | if (columnName == null) throw new ArgumentNullException("columnName"); 36 | if (fieldName == null) throw new ArgumentNullException("fieldName"); 37 | 38 | TargetType = targetType; 39 | ColumnName = columnName; 40 | FieldName = fieldName; 41 | } 42 | #endregion 43 | 44 | #region Properties 45 | /// 46 | /// Gets the type of the object being read or null to match all object types. 47 | /// 48 | public Type TargetType { get; private set; } 49 | 50 | /// 51 | /// Gets the name of the column to map. 52 | /// 53 | public string ColumnName { get; private set; } 54 | 55 | /// 56 | /// Gets the name of the target field or property. 57 | /// 58 | public string FieldName { get; private set; } 59 | #endregion 60 | } 61 | 62 | /// 63 | /// Represents a column mapping that overrides the default rules. 64 | /// ColumnOverrides are processed after any default rules are processed. 65 | /// 66 | /// The type of object to apply the mapping to. 67 | [SuppressMessage("Microsoft.StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "The classes are related by implementing multiple generic signatures.")] 68 | public class ColumnOverride : ColumnOverride 69 | { 70 | /// 71 | /// Initializes a new instance of the ColumnOverride class. 72 | /// 73 | /// The name of the column to map. 74 | /// The name of the field to map to. 75 | public ColumnOverride(string columnName, string fieldName) 76 | : base(typeof(T), columnName, fieldName) 77 | { 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Insight.Database.Core/Structure/DerivedResultsReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | namespace Insight.Database.Structure 10 | { 11 | /// 12 | /// Can read classes derived from the Results class. 13 | /// 14 | /// The type that can be read. 15 | public class DerivedResultsReader : IQueryReader where T : Results, new() 16 | { 17 | /// 18 | /// The default reader for this type. 19 | /// 20 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] 21 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")] 22 | public static readonly IQueryReader Default = new DerivedResultsReader(); 23 | 24 | /// 25 | public Type ReturnType { get { return typeof(T); } } 26 | 27 | /// 28 | public T Read(IDbCommand command, IDataReader reader) 29 | { 30 | var t = new T(); 31 | t.Read(command, reader); 32 | return t; 33 | } 34 | 35 | /// 36 | public async Task ReadAsync(IDbCommand command, IDataReader reader, CancellationToken cancellationToken) 37 | { 38 | var t = new T(); 39 | await t.ReadAsync(command, reader, cancellationToken).ConfigureAwait(false); 40 | return t; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Insight.Database.Core/Structure/Guardian.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | #pragma warning disable 0649 9 | 10 | namespace Insight.Database.Structure 11 | { 12 | /// 13 | /// Helps a child object find its parent. 14 | /// It knows the parent's ID by getting it from the recordset. 15 | /// 16 | /// The type of the child. 17 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "These are related generic classes.")] 18 | public abstract class Guardian 19 | { 20 | /// 21 | /// Gets or sets the child object. 22 | /// 23 | public TChild Object { get; set; } 24 | 25 | /// 26 | /// Reads the current record from the data reader. 27 | /// 28 | /// The reader to read from. 29 | public virtual void ReadCurrent(IDataReader reader) 30 | { 31 | } 32 | 33 | /// 34 | /// Returns the ID portion of the guardian. 35 | /// 36 | /// The ID of the guardian. 37 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] 38 | public abstract object GetID(); 39 | } 40 | 41 | /// 42 | /// Helps a child object find its parent. 43 | /// It knows the parent's ID by getting it from the recordset. 44 | /// 45 | /// The type of the child. 46 | /// The type of the ID. 47 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "These are related generic classes.")] 48 | public class Guardian : Guardian 49 | { 50 | /// 51 | /// Gets or sets the ID of the parent. This is assumed to be the first column in the recordset. 52 | /// 53 | [Column("*1")] 54 | public TId ParentId1 { get; set; } 55 | 56 | /// 57 | public override void ReadCurrent(IDataReader reader) 58 | { 59 | if (reader == null) throw new ArgumentNullException("reader"); 60 | 61 | base.ReadCurrent(reader); 62 | ParentId1 = (TId)reader[0]; 63 | } 64 | 65 | /// 66 | public override object GetID() 67 | { 68 | return ParentId1; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Insight.Database.Core/Structure/Guardian.tt: -------------------------------------------------------------------------------- 1 | // 2 | <#@ template debug="false" hostspecific="false" language="C#" #> 3 | <#@ assembly name="System.Core" #> 4 | <#@ import namespace="System.Linq" #> 5 | <#@ import namespace="System.Text" #> 6 | <#@ import namespace="System.Collections.Generic" #> 7 | <#@ output extension="generated.cs" #> 8 | <#@ include file="..\GenericTypes.tt" #> 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Data; 12 | using System.Threading; 13 | using System.Threading.Tasks; 14 | using Insight.Database.Structure; 15 | 16 | namespace Insight.Database.Structure 17 | { 18 | <# 19 | for (var typeCount = 2; typeCount <= 7; typeCount++) 20 | { 21 | var parentFieldList = GetGenericList(typeCount, "ParentId{0}"); 22 | var baseTypeList = GetGenericList(typeCount - 1, GenericTypeFormat); 23 | var typeList = GetGenericList(typeCount, GenericTypeFormat); 24 | #> 25 | /// 26 | /// Helps a child object find its parent. 27 | /// It knows the parent's ID by getting it from the recordset. 28 | /// 29 | /// The type of the child. 30 | <# for (var typeIndex = 1; typeIndex <= typeCount; typeIndex++) { #> 31 | /// The type of the data in the <#= ConvertToOrdinal(typeIndex) #> subobject. 32 | <# } #> 33 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1501:AvoidExcessiveInheritance")] 34 | public class Guardian> : Guardian> 35 | { 36 | /// 37 | /// Gets or sets the ID of the parent. This is assumed to be the first column in the recordset. 38 | /// 39 | [Column("*<#= typeCount #>")] 40 | public T<#= typeCount #> ParentId<#= typeCount #> { get; set; } 41 | 42 | /// 43 | public override void ReadCurrent(IDataReader reader) 44 | { 45 | if (reader == null) throw new ArgumentNullException("reader"); 46 | 47 | base.ReadCurrent(reader); 48 | ParentId<#= typeCount #> = (T<#= typeCount #>)reader[<#= typeCount - 1 #>]; 49 | } 50 | 51 | /// 52 | public override object GetID() 53 | { 54 | return Tuple.Create(<#= parentFieldList #>); 55 | } 56 | } 57 | 58 | <# } #> 59 | } 60 | -------------------------------------------------------------------------------- /Insight.Database.Core/Structure/IChildMapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Insight.Database.Structure 8 | { 9 | /// 10 | /// An instance of IMapper can map TChild to somewhere inside TRoot. 11 | /// This is a one-to-many mapping, and TChild may go into sublayers of the structure. 12 | /// 13 | /// The type of the root object. 14 | /// The type of the child object. 15 | /// The type of the ID value. 16 | interface IChildMapper 17 | { 18 | /// 19 | /// Maps a list of children into the structure. 20 | /// 21 | /// The list of root objects. 22 | /// The list of child objects with their parent's id. 23 | void MapChildren(IEnumerable roots, IEnumerable> children); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Insight.Database.Core/Structure/IChildRecordReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | namespace Insight.Database.Structure 10 | { 11 | /// 12 | /// Knows how to read a record containing an object and its parent id. 13 | /// 14 | /// The type of object to read. 15 | /// The type of ID for the object. 16 | public interface IChildRecordReader 17 | { 18 | /// 19 | /// Reads children from the reader. 20 | /// 21 | /// The reader. 22 | /// The children grouped by id. 23 | IEnumerable> Read(IDataReader reader); 24 | 25 | /// 26 | /// Asynchronously reads children from the reader. 27 | /// 28 | /// The reader. 29 | /// A token that can be used to cancel the operation. 30 | /// A task containing the children grouped by id. 31 | Task>> ReadAsync(IDataReader reader, CancellationToken cancellationToken); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Insight.Database.Core/Structure/IQueryReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | namespace Insight.Database.Structure 10 | { 11 | /// 12 | /// Describes an object that can read the results of a query. 13 | /// 14 | public interface IQueryReader 15 | { 16 | /// 17 | /// Gets the type of object returned from the query reader. 18 | /// 19 | /// The type of object returned from the query reader. 20 | /// This is used to support dynamic calls. 21 | Type ReturnType { get; } 22 | } 23 | 24 | /// 25 | /// Describes an object that can read the results of a query. 26 | /// 27 | /// The type of object read from the data raeder. 28 | public interface IQueryReader : IQueryReader 29 | { 30 | /// 31 | /// Reads objects from the reader. 32 | /// 33 | /// The command that was executed. 34 | /// The reader to read from. 35 | /// The output of the reader. 36 | T Read(IDbCommand command, IDataReader reader); 37 | 38 | /// 39 | /// Reads objects from the reader asynchronously. 40 | /// 41 | /// The command that was executed. 42 | /// The reader to read from. 43 | /// An optional CancellationToken that can cancel the operation. 44 | /// The output of the reader. 45 | Task ReadAsync(IDbCommand command, IDataReader reader, CancellationToken cancellationToken); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Insight.Database.Core/Structure/IRecordIdAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Insight.Database.Structure 8 | { 9 | /// 10 | /// Common functions of the RecordIdAttribute classes. 11 | /// 12 | interface IRecordIdAttribute 13 | { 14 | /// 15 | /// Gets the order of the record ids. 16 | /// 17 | int Order { get; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Insight.Database.Core/Structure/IRecordReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Insight.Database.CodeGenerator; 8 | 9 | namespace Insight.Database.Structure 10 | { 11 | /// 12 | /// An object that can read a record. 13 | /// It needs to be equatable so serializer caching works properly. 14 | /// 15 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1040:AvoidEmptyInterfaces")] 16 | public interface IRecordReader : IEquatable 17 | { 18 | } 19 | 20 | /// 21 | /// An object that can read a record of a given type. 22 | /// 23 | /// The type of record that can be read. 24 | public interface IRecordReader : IRecordReader 25 | { 26 | /// 27 | /// Gets a value indicating whether the recordreader needs its records de-duplicated. 28 | /// 29 | /// True if the query reader should de-duplicate the records. 30 | bool RequiresDeduplication { get; } 31 | 32 | /// 33 | /// Gets a function that can read a record from the given data reader. 34 | /// 35 | /// The reader to be read from. 36 | /// A function that can read a record. 37 | /// This returns a function because each reader may have a different schema. 38 | Func GetRecordReader(IDataReader reader); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Insight.Database.Core/Structure/IRecordStructure.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Insight.Database.Mapping; 7 | 8 | namespace Insight.Database.Structure 9 | { 10 | /// 11 | /// Tells the serialization engine the structure of a record. 12 | /// 13 | interface IRecordStructure : IRecordReader, IColumnMapper 14 | { 15 | /// 16 | /// Gets a list of the subtypes in the record. 17 | /// 18 | /// The list of the subtypes in the record. 19 | Type[] GetObjectTypes(); 20 | 21 | /// 22 | /// Gets a mapping of types to column names. The column names are used to override how Insight splits records into objects. 23 | /// 24 | /// A mapping of the split columns or null to use the default. 25 | IDictionary GetSplitColumns(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Insight.Database.Core/Structure/MultiReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Insight.Database.Structure 9 | { 10 | /// 11 | /// A RecordReader that can read more than one type of class from a record stream. 12 | /// 13 | /// The base type of all of the records returned. 14 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Multi")] 15 | public class MultiReader : IRecordReader 16 | { 17 | /// 18 | /// The selector for the record readers. 19 | /// 20 | private Func> _selector; 21 | 22 | /// 23 | /// Initializes a new instance of the MultiReader class. 24 | /// 25 | /// The function used to select the record reader for an individual record. 26 | public MultiReader(Func> selector) 27 | { 28 | _selector = selector; 29 | } 30 | 31 | /// 32 | public virtual bool RequiresDeduplication { get { return false; } } 33 | 34 | /// 35 | public Func GetRecordReader(IDataReader reader) 36 | { 37 | return r => 38 | { 39 | // wrap the reader so we can go out of order when reading the columns 40 | using (var wrapped = new CachedDbDataReader(reader)) 41 | { 42 | return _selector(wrapped).GetRecordReader(wrapped)(wrapped); 43 | } 44 | }; 45 | } 46 | 47 | /// 48 | public bool Equals(IRecordReader other) 49 | { 50 | return other is MultiReader; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Insight.Database.Core/Structure/ParentRecordIdAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Insight.Database.Structure; 7 | 8 | namespace Insight.Database 9 | { 10 | /// 11 | /// Marks a field or property as the one Insight should consider as the ID field. 12 | /// 13 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] 14 | public sealed class ParentRecordIdAttribute : Attribute, IRecordIdAttribute 15 | { 16 | /// 17 | /// Initializes a new instance of the ParentRecordIdAttribute class. 18 | /// 19 | public ParentRecordIdAttribute() 20 | { 21 | } 22 | 23 | /// 24 | /// Initializes a new instance of the ParentRecordIdAttribute class. 25 | /// 26 | /// An index representing the order the Ids should be evaluated. 27 | public ParentRecordIdAttribute(int order) 28 | { 29 | Order = order; 30 | } 31 | 32 | /// 33 | /// Gets the order of the ID field. 34 | /// 35 | public int Order { get; private set; } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Insight.Database.Core/Structure/PostProcessRecordReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Insight.Database.Structure; 8 | 9 | namespace Insight.Database 10 | { 11 | /// 12 | /// Extends a record reader, allowing records to be postprocessed after reading them. 13 | /// 14 | /// The type of record that is being read. 15 | public class PostProcessRecordReader : RecordReader 16 | { 17 | /// 18 | /// The base reader to read the record. 19 | /// 20 | private IRecordReader _baseReader; 21 | 22 | /// 23 | /// The code to execute after reading the record. 24 | /// 25 | private Func _postRead; 26 | 27 | /// 28 | /// Initializes a new instance of the PostProcessRecordReader class. 29 | /// 30 | /// The code to execute after reading the record. 31 | public PostProcessRecordReader(Func postRead) 32 | : this(OneToOne.Records, postRead) 33 | { 34 | } 35 | 36 | /// 37 | /// Initializes a new instance of the PostProcessRecordReader class. 38 | /// 39 | /// The base record reader to use to read the record. 40 | /// The code to execute after reading the record. 41 | public PostProcessRecordReader(IRecordReader baseReader, Func postRead) 42 | { 43 | _baseReader = baseReader; 44 | _postRead = postRead; 45 | } 46 | 47 | /// 48 | public override IRecordReader GetGuardianReader() 49 | { 50 | return new PostProcessRecordReader( 51 | OneToOne.Records, 52 | (IDataReader reader, TGuardian guardian) => 53 | { 54 | _postRead(reader, guardian.Object); 55 | return guardian; 56 | }); 57 | } 58 | 59 | /// 60 | public override Func GetRecordReader(IDataReader reader) 61 | { 62 | var baseReader = _baseReader.GetRecordReader(reader); 63 | 64 | return r => 65 | { 66 | using (var wrapped = new CachedDbDataReader(r)) 67 | { 68 | var t = baseReader(wrapped); 69 | return _postRead(wrapped, t); 70 | } 71 | }; 72 | } 73 | 74 | /// 75 | public override bool Equals(IRecordReader other) 76 | { 77 | return true; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Insight.Database.Core/Structure/QueryReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | namespace Insight.Database.Structure 10 | { 11 | /// 12 | /// Reads objects from a Data Reader. 13 | /// 14 | /// The type of object read from the reader. 15 | public abstract class QueryReader 16 | { 17 | #region Fields 18 | /// 19 | /// The set of child records to read from the stream. 20 | /// 21 | private List> _children; 22 | #endregion 23 | 24 | #region Constructors 25 | /// 26 | /// Initializes a new instance of the QueryReader class. 27 | /// 28 | /// A mapping that can be used to read a single record from the stream. 29 | protected QueryReader(IRecordReader recordReader) 30 | { 31 | RecordReader = recordReader; 32 | _children = new List>(); 33 | } 34 | #endregion 35 | 36 | #region Properties 37 | /// 38 | /// Gets the mapping used to read an individual record from this stream. 39 | /// 40 | protected IRecordReader RecordReader { get; private set; } 41 | #endregion 42 | 43 | #region Methods 44 | /// 45 | /// Adds a child reader to this reader. 46 | /// 47 | /// The child reader to add. 48 | /// A QueryReader that also reads the specified children. 49 | internal QueryReader AddChild(Children child) 50 | { 51 | var clone = (QueryReader)MemberwiseClone(); 52 | clone._children = _children.ToList(); 53 | clone._children.Add(child); 54 | 55 | return clone; 56 | } 57 | 58 | /// 59 | /// Reads all of the children from the stream. 60 | /// 61 | /// The data reader to read from. 62 | /// The results of the current read operation. 63 | protected void ReadChildren(IDataReader reader, IEnumerable results) 64 | { 65 | // read in the children 66 | foreach (var child in _children) 67 | child.Read(results, reader); 68 | } 69 | 70 | /// 71 | /// Reads all of the children from the stream asynchronously. 72 | /// 73 | /// The data reader to read from. 74 | /// The results of the current read operation. 75 | /// A token that can be used to cancel the operation. 76 | /// A task representing the completion of the read. 77 | protected async Task ReadChildrenAsync(IDataReader reader, IEnumerable results, CancellationToken cancellationToken) 78 | { 79 | // read in the children 80 | foreach (var child in _children) 81 | await child.ReadAsync(results, reader, cancellationToken).ConfigureAwait(false); 82 | } 83 | #endregion 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Insight.Database.Core/Structure/RecordIdAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Insight.Database.Structure; 7 | 8 | namespace Insight.Database 9 | { 10 | /// 11 | /// Marks a field or property as the one Insight should consider as the ID field. 12 | /// 13 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] 14 | public sealed class RecordIdAttribute : Attribute, IRecordIdAttribute 15 | { 16 | /// 17 | /// Initializes a new instance of the RecordIdAttribute class. 18 | /// 19 | public RecordIdAttribute() 20 | { 21 | } 22 | 23 | /// 24 | /// Initializes a new instance of the RecordIdAttribute class. 25 | /// 26 | /// An index representing the order the Ids should be evaluated. 27 | public RecordIdAttribute(int order) 28 | { 29 | Order = order; 30 | } 31 | 32 | /// 33 | /// Gets the order of the ID field. 34 | /// 35 | public int Order { get; private set; } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Insight.Database.Core/Structure/RecordReader.tt: -------------------------------------------------------------------------------- 1 | // 2 | <#@ template debug="false" hostspecific="false" language="C#" #> 3 | <#@ assembly name="System.Core" #> 4 | <#@ import namespace="System.Linq" #> 5 | <#@ import namespace="System.Text" #> 6 | <#@ import namespace="System.Collections.Generic" #> 7 | <#@ output extension="generated.cs" #> 8 | <#@ include file="..\GenericTypes.tt" #> 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Data; 12 | using System.Linq; 13 | using System.Threading; 14 | using System.Threading.Tasks; 15 | using Insight.Database.Structure; 16 | 17 | namespace Insight.Database.Structure 18 | { 19 | /// 20 | /// A base implementation of IRecordReader. 21 | /// 22 | public abstract partial class RecordReader : IRecordReader 23 | { 24 | <# 25 | for (var typeCount = 2; typeCount <= 7; typeCount++) 26 | { 27 | var parentFieldList = GetGenericList(typeCount, "g.ParentId{0}"); 28 | var typeList = GetGenericList(typeCount, GenericTypeFormat); 29 | #> 30 | /// 31 | /// Returns a child record reader that reads this type of record and groups by the first column in the recordset. 32 | /// 33 | <# for (var typeIndex = 1; typeIndex <= typeCount; typeIndex++) { #> 34 | /// The type of the <#= ConvertToOrdinal(typeIndex) #> ID. 35 | <# } #> 36 | /// A child record reader. 37 | public IChildRecordReader>> GroupByColumns<<#= typeList #>>() 38 | { 39 | return new ChildRecordReader>, Tuple<<#= typeList #>>, T>( 40 | GetGuardianReader>>(), 41 | records => records.GroupBy(g => Tuple.Create(<#= parentFieldList #>), g => g.Object)); 42 | } 43 | 44 | <# } #> 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Insight.Database.Core/Structure/SingleChildMapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Insight.Database.Structure 8 | { 9 | /// 10 | /// Maps a child into a single parent. 11 | /// 12 | /// The type of the parent object. 13 | /// The type of the child object. 14 | class SingleChildMapper 15 | { 16 | /// 17 | /// The default action that sets the children into the proper parent. 18 | /// 19 | private Lazy>> _defaultListSetter = new Lazy>>(GetListSetter); 20 | 21 | /// 22 | /// The action that sets the children into the proper parent. 23 | /// 24 | private Action> _listSetter; 25 | 26 | /// 27 | /// Initializes a new instance of the SingleChildMapper class. 28 | /// 29 | /// The function that can be used to set the list into the parent. 30 | public SingleChildMapper(Action> listSetter) 31 | { 32 | _listSetter = listSetter ?? _defaultListSetter.Value; 33 | } 34 | 35 | /// 36 | /// Maps the children into the parent object. 37 | /// 38 | /// The list of parents. This must have a single item. 39 | /// The list of children. 40 | public void MapChildren(IEnumerable roots, IEnumerable children) 41 | { 42 | var single = roots.SingleOrDefault(); 43 | 44 | if (single == null) 45 | { 46 | if (children.Any()) 47 | throw new InvalidOperationException("Child records were returned, but there was no parent record."); 48 | 49 | return; 50 | } 51 | 52 | _listSetter(single, children.ToList()); 53 | } 54 | 55 | /// 56 | /// Gets the list setter for the class, looking for an IList that matches the type. 57 | /// 58 | /// An accessor for the ID field. 59 | private static Action> GetListSetter() 60 | { 61 | return ChildMapperHelper.GetListAccessor(typeof(TParent), typeof(TChild)).CreateSetMethod>(); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Insight.Database.Core/Structure/SingleChildren.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | namespace Insight.Database.Structure 10 | { 11 | /// 12 | /// Knows how to read a list of children into a parent object. 13 | /// 14 | /// The type of the parent object. 15 | /// The type of the child object. 16 | class SingleChildren : Children 17 | { 18 | /// 19 | /// Can read the child records from the stream. 20 | /// 21 | private IRecordReader _recordReader; 22 | 23 | /// 24 | /// Can map the children into the parent in the right place. 25 | /// 26 | private SingleChildMapper _mapper; 27 | 28 | /// 29 | /// Initializes a new instance of the SingleChildren class. 30 | /// 31 | /// The recordReader that can read the children from the data stream. 32 | /// The mapper that puts the children into the parent. 33 | public SingleChildren(IRecordReader recordReader, SingleChildMapper mapper) 34 | { 35 | _recordReader = recordReader; 36 | _mapper = mapper; 37 | } 38 | 39 | /// 40 | public override void Read(IEnumerable parents, IDataReader reader) 41 | { 42 | _mapper.MapChildren(parents, reader.ToList(_recordReader)); 43 | } 44 | 45 | /// 46 | public override async Task ReadAsync(IEnumerable parents, IDataReader reader, CancellationToken ct) 47 | { 48 | var result = await reader.ToListAsync(_recordReader).ConfigureAwait(false); 49 | _mapper.MapChildren(parents, result); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Insight.Database.Core/Structure/SingleReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using Insight.Database.Structure; 9 | 10 | namespace Insight.Database.Structure 11 | { 12 | /// 13 | /// Reads a single record from a data stream. 14 | /// 15 | /// The type of object to read. 16 | public class SingleReader : QueryReader, IQueryReader 17 | { 18 | #region Fields 19 | /// 20 | /// The default reader to read a list of type T. 21 | /// 22 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] 23 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")] 24 | public static readonly SingleReader Default = new SingleReader(); 25 | #endregion 26 | 27 | #region Constructors 28 | /// 29 | /// Initializes a new instance of the SingleReader class. 30 | /// 31 | public SingleReader() : this(OneToOne.Records) 32 | { 33 | } 34 | 35 | /// 36 | /// Initializes a new instance of the SingleReader class. 37 | /// 38 | /// The mapping to use to read objects from each record. 39 | public SingleReader(IRecordReader mapping) : base(mapping) 40 | { 41 | } 42 | #endregion 43 | 44 | #region Methods 45 | /// 46 | public virtual Type ReturnType { get { return typeof(T); } } 47 | 48 | /// 49 | public T Read(IDbCommand command, IDataReader reader) 50 | { 51 | var results = reader.Single(RecordReader); 52 | 53 | // read in the children 54 | ReadChildren(reader, Enumerable.Range(1, 1).Select(i => results)); 55 | 56 | return results; 57 | } 58 | 59 | /// 60 | public async Task ReadAsync(IDbCommand command, IDataReader reader, CancellationToken cancellationToken) 61 | { 62 | IList results = await reader.ToListAsync(RecordReader, cancellationToken, firstRecordOnly: true).ConfigureAwait(false); 63 | await ReadChildrenAsync(reader, results, cancellationToken).ConfigureAwait(false); 64 | return results.FirstOrDefault(); 65 | } 66 | 67 | /// 68 | /// Adds a child reader to this reader. 69 | /// 70 | /// The child reader to add. 71 | /// A list reader that also reads the specified child. 72 | internal new SingleReader AddChild(Children child) 73 | { 74 | return (SingleReader)base.AddChild(child); 75 | } 76 | #endregion 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Insight.Database.Core/Structure/SingleReader.tt: -------------------------------------------------------------------------------- 1 | // 2 | <#@ template debug="false" hostspecific="false" language="C#" #> 3 | <#@ assembly name="System.Core" #> 4 | <#@ import namespace="System.Linq" #> 5 | <#@ import namespace="System.Text" #> 6 | <#@ import namespace="System.Collections.Generic" #> 7 | <#@ output extension="generated.cs" #> 8 | <#@ include file="..\GenericTypes.tt" #> 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Data; 12 | using System.Threading; 13 | using System.Threading.Tasks; 14 | using Insight.Database.Structure; 15 | 16 | namespace Insight.Database.Structure 17 | { 18 | <# 19 | for (var typeCount = 2; typeCount <= GenericTypeMax; typeCount++) 20 | { 21 | var typeList = GetGenericList(typeCount, GenericTypeFormat); 22 | #> 23 | /// 24 | /// Reads a single record from a data stream. 25 | /// 26 | <# for (var typeIndex = 1; typeIndex <= typeCount; typeIndex++) { #> 27 | /// The type of the data in the <#= ConvertToOrdinal(typeIndex) #> subobject. 28 | <# } #> 29 | public class SingleReader<<#= typeList #>> : SingleReader 30 | { 31 | #region Fields 32 | /// 33 | /// The default reader to read a list of type T. 34 | /// 35 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")] 36 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] 37 | public static readonly new SingleReader<<#= typeList #>> Default = new SingleReader<<#= typeList #>>(); 38 | #endregion 39 | 40 | #region Constructors 41 | /// 42 | /// Initializes a new instance of the SingleReader class. 43 | /// 44 | public SingleReader() : base(OneToOne<<#= typeList #>>.Records) 45 | { 46 | } 47 | #endregion 48 | } 49 | 50 | <# } #> 51 | } 52 | -------------------------------------------------------------------------------- /Insight.Database.Json/GlobalSuppressions.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonwagner/Insight.Database/cf55c24cda26a2300f9270a5edcee6ebe473f9f8/Insight.Database.Json/GlobalSuppressions.cs -------------------------------------------------------------------------------- /Insight.Database.Json/Insight.Database.Json.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Replaces the DataContractJsonSerializer that Insight.Database uses with Newtonsoft.Json (JSON.NET). 5 | 6 | Insight orm sql micro-orm sqlserver db2 glimpse miniprofiler mysql oracle postgres sybase sybasease sqlite sqllite 7 | 8 | 9 | 10 | net8.0;netstandard2.0;net48 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Insight.Database.Json/JsonNetObjectSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Newtonsoft.Json; 8 | 9 | namespace Insight.Database.Json 10 | { 11 | /// 12 | /// Handles serializing objects to JSON by using the JSON.NET serializer. 13 | /// 14 | public class JsonNetObjectSerializer : DbObjectSerializer 15 | { 16 | /// 17 | /// Initializes the JSON.NET serializer. 18 | /// 19 | public static void Initialize() 20 | { 21 | // override the type used to serialize 22 | Insight.Database.JsonObjectSerializer.OverrideSerializer = new JsonNetObjectSerializer(); 23 | } 24 | 25 | /// 26 | public override bool CanSerialize(Type type, DbType dbType) 27 | { 28 | return base.CanSerialize(type, dbType) || dbType == DbType.Object; 29 | } 30 | 31 | /// 32 | /// Serializes an object to a string. 33 | /// 34 | /// The type of the object. 35 | /// The object to serialize. 36 | /// The serialized representation of the object. 37 | public override object SerializeObject(Type type, object value) 38 | { 39 | if (value == null) 40 | return null; 41 | 42 | return JsonConvert.SerializeObject(value, type, Formatting.None, null); 43 | } 44 | 45 | /// 46 | /// Deserializes an object from a string. 47 | /// 48 | /// The type of object to deserialize. 49 | /// The encoded value of the object. 50 | /// The deserialized object. 51 | public override object DeserializeObject(Type type, object encoded) 52 | { 53 | return JsonConvert.DeserializeObject((string)encoded, type); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Insight.Database.Json/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonwagner/Insight.Database/cf55c24cda26a2300f9270a5edcee6ebe473f9f8/Insight.Database.Json/Properties/AssemblyInfo.cs -------------------------------------------------------------------------------- /Insight.Database.Providers.Default/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "Insight.Database.Providers")] 2 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "Insight.Database.Reliable")] 3 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "Insight.Database")] 4 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Scope = "member", Target = "Insight.Database.SqlInsightDbProvider.#PrepareBulkCopy(System.Data.IDbConnection,System.String,System.Data.IDataReader,System.Action`1,Insight.Database.InsightBulkCopyOptions,System.Data.IDbTransaction)")] 5 | -------------------------------------------------------------------------------- /Insight.Database.Providers.Default/Insight.Database.Providers.Default.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | The SQLServer, ODBC, and OLEDB providers for Insight.Database. 4 | These are legacy providers. Recommend use Insight.Database.Providers.MsSqlServer. 5 | Insight orm sql micro-orm sqlserver odbc oledb 6 | 7 | 8 | 9 | LibraryPackage 10 | 11 | 12 | 13 | net8.0;netstandard2.0;net48 14 | 612,618 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Insight.Database.Providers.Default/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonwagner/Insight.Database/cf55c24cda26a2300f9270a5edcee6ebe473f9f8/Insight.Database.Providers.Default/Properties/AssemblyInfo.cs -------------------------------------------------------------------------------- /Insight.Database.Providers.Default/ReliableConnectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | #if !NO_CONNECTION_SETTINGS 4 | using System.Configuration; 5 | #endif 6 | using System.Data.SqlClient; 7 | using System.Linq; 8 | using System.Text; 9 | using Insight.Database; 10 | 11 | namespace Insight.Database.Reliable 12 | { 13 | /// 14 | /// Extension methods for Reliable Connections. 15 | /// 16 | public static class ReliableConnectionExtensions 17 | { 18 | #region SqlConnectionStringBuilder Extensions 19 | /// 20 | /// Creates and returns a new SqlConnection. 21 | /// 22 | /// The SqlConnectionStringBuilder containing the connection string. 23 | /// A closed SqlConnection. 24 | public static ReliableConnection ReliableConnection(this SqlConnectionStringBuilder builder) 25 | { 26 | if (builder == null) 27 | { 28 | throw new ArgumentNullException("builder", "SqlConnectionStringBuilder cannot be null"); 29 | } 30 | 31 | return new ReliableConnection(builder.ConnectionString); 32 | } 33 | 34 | /// 35 | /// Opens and returns a database connection. 36 | /// 37 | /// The connection string to open and return. 38 | /// The opened connection. 39 | public static ReliableConnection ReliableOpen(this SqlConnectionStringBuilder builder) 40 | { 41 | return builder.ReliableConnection().OpenConnection(); 42 | } 43 | 44 | /// 45 | /// Converts the connection to a connection that can be invoked dynamically to return lists of FastExpando. 46 | /// 47 | /// The connection to use. 48 | /// A DynamicConnection using the given connection. 49 | public static dynamic ReliableDynamic(this SqlConnectionStringBuilder builder) 50 | { 51 | return builder.ReliableConnection().Dynamic(); 52 | } 53 | 54 | /// 55 | /// Converts the connection to a connection that can be invoked dynamically to return lists of type T. 56 | /// 57 | /// The connection to use. 58 | /// The type of object to return from queries. 59 | /// A DynamicConnection using the given connection. 60 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")] 61 | public static dynamic ReliableDynamic(this SqlConnectionStringBuilder builder) 62 | { 63 | return builder.ReliableConnection().Dynamic(); 64 | } 65 | #endregion 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Insight.Database.Providers.MiniProfiler/GlobalSuppressions.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonwagner/Insight.Database/cf55c24cda26a2300f9270a5edcee6ebe473f9f8/Insight.Database.Providers.MiniProfiler/GlobalSuppressions.cs -------------------------------------------------------------------------------- /Insight.Database.Providers.MiniProfiler/Insight.Database.Providers.MiniProfiler.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | An Insight.Database provider for MiniProfiler profiling framework so the two can play together nicely. 5 | 6 | Insight orm sql micro-orm profiler miniprofiler 7 | 8 | 9 | 10 | LibraryPackage 11 | 12 | 13 | 14 | net8.0;netstandard2.0;net48 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Insight.Database.Providers.MiniProfiler/MiniProfilerInsightDbProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Data.Common; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using StackExchange.Profiling.Data; 9 | 10 | namespace Insight.Database.Providers.MiniProfiler 11 | { 12 | /// 13 | /// Implements the Insight provider for MiniProfiler connections. 14 | /// 15 | public class MiniProfilerInsightDbProvider : WrappedInsightDbProvider 16 | { 17 | /// 18 | /// Gets the types of objects that this provider supports. 19 | /// Include connectionstrings, connections, commands, and readers. 20 | /// 21 | public override IEnumerable SupportedTypes 22 | { 23 | get 24 | { 25 | return new Type[] { typeof(ProfiledDbConnection), typeof(ProfiledDbCommand) }; 26 | } 27 | } 28 | 29 | /// 30 | /// Registers this provider. This is generally not needed, unless you want to force an assembly reference to this provider. 31 | /// 32 | public static void RegisterProvider() 33 | { 34 | InsightDbProvider.RegisterProvider(new MiniProfilerInsightDbProvider()); 35 | } 36 | 37 | /// 38 | public override IDbConnection CloneDbConnection(IDbConnection connection) 39 | { 40 | if (connection == null) throw new ArgumentNullException("connection"); 41 | 42 | // clone the inner connection 43 | var innerConnection = GetInnerConnection(connection); 44 | var innerProvider = InsightDbProvider.For(innerConnection); 45 | var clonedInnerConnection = (DbConnection)innerProvider.CloneDbConnection(innerConnection); 46 | 47 | var profiledConnection = (ProfiledDbConnection)connection; 48 | return new ProfiledDbConnection(clonedInnerConnection, profiledConnection.Profiler); 49 | } 50 | 51 | /// 52 | /// Unwraps the given connection and returns the inner connection. 53 | /// 54 | /// The outer connection. 55 | /// The inner connection. 56 | public override IDbConnection GetInnerConnection(IDbConnection connection) 57 | { 58 | ProfiledDbConnection profiledConnection = (ProfiledDbConnection)connection; 59 | return profiledConnection.WrappedConnection; 60 | } 61 | 62 | /// 63 | /// Unwraps the given command and returns the inner command. 64 | /// 65 | /// The outer command. 66 | /// The inner command. 67 | public override IDbCommand GetInnerCommand(IDbCommand command) 68 | { 69 | if (command == null) throw new ArgumentNullException("command"); 70 | 71 | ProfiledDbCommand profiledCommand = (ProfiledDbCommand)command; 72 | return profiledCommand.WrappedCommand; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Insight.Database.Providers.MiniProfiler/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonwagner/Insight.Database/cf55c24cda26a2300f9270a5edcee6ebe473f9f8/Insight.Database.Providers.MiniProfiler/Properties/AssemblyInfo.cs -------------------------------------------------------------------------------- /Insight.Database.Providers.MsSqlClient/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "Insight.Database.Providers")] 2 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "Insight.Database.Reliable")] 3 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "Insight.Database")] 4 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Scope = "member", Target = "Insight.Database.SqlInsightDbProvider.#PrepareBulkCopy(System.Data.IDbConnection,System.String,System.Data.IDataReader,System.Action`1,Insight.Database.InsightBulkCopyOptions,System.Data.IDbTransaction)")] 5 | -------------------------------------------------------------------------------- /Insight.Database.Providers.MsSqlClient/Insight.Database.Providers.MsSqlClient.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | The SQL Server provider that depends on the Microsoft.Sql.DataClient nuget package. 4 | Insight orm sql micro-orm sqlserver 5 | 6 | 7 | 8 | LibraryPackage 9 | 10 | 11 | 12 | net8.0;netstandard2.0;net48 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Insight.Database.Providers.MsSqlClient/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | 6 | [assembly: AssemblyCopyright("Copyright � Jon Wagner, used by permission")] 7 | [assembly: CLSCompliant(false)] 8 | -------------------------------------------------------------------------------- /Insight.Database.Providers.MsSqlClient/ReliableConnectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | #if !NO_CONNECTION_SETTINGS 4 | using System.Configuration; 5 | #endif 6 | using System.Linq; 7 | using System.Text; 8 | using Insight.Database; 9 | using Microsoft.Data.SqlClient; 10 | 11 | namespace Insight.Database.Reliable 12 | { 13 | /// 14 | /// Extension methods for Reliable Connections. 15 | /// 16 | public static class ReliableConnectionExtensions 17 | { 18 | #region SqlConnectionStringBuilder Extensions 19 | /// 20 | /// Creates and returns a new SqlConnection. 21 | /// 22 | /// The SqlConnectionStringBuilder containing the connection string. 23 | /// A closed SqlConnection. 24 | public static ReliableConnection ReliableConnection(this SqlConnectionStringBuilder builder) 25 | { 26 | if (builder == null) 27 | { 28 | throw new ArgumentNullException("builder", "SqlConnectionStringBuilder cannot be null"); 29 | } 30 | 31 | return new ReliableConnection(builder.ConnectionString); 32 | } 33 | 34 | /// 35 | /// Opens and returns a database connection. 36 | /// 37 | /// The connection string to open and return. 38 | /// The opened connection. 39 | public static ReliableConnection ReliableOpen(this SqlConnectionStringBuilder builder) 40 | { 41 | return builder.ReliableConnection().OpenConnection(); 42 | } 43 | 44 | /// 45 | /// Converts the connection to a connection that can be invoked dynamically to return lists of FastExpando. 46 | /// 47 | /// The connection to use. 48 | /// A DynamicConnection using the given connection. 49 | public static dynamic ReliableDynamic(this SqlConnectionStringBuilder builder) 50 | { 51 | return builder.ReliableConnection().Dynamic(); 52 | } 53 | 54 | /// 55 | /// Converts the connection to a connection that can be invoked dynamically to return lists of type T. 56 | /// 57 | /// The connection to use. 58 | /// The type of object to return from queries. 59 | /// A DynamicConnection using the given connection. 60 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")] 61 | public static dynamic ReliableDynamic(this SqlConnectionStringBuilder builder) 62 | { 63 | return builder.ReliableConnection().Dynamic(); 64 | } 65 | #endregion 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Insight.Database.Providers.MySql/GlobalSuppressions.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonwagner/Insight.Database/cf55c24cda26a2300f9270a5edcee6ebe473f9f8/Insight.Database.Providers.MySql/GlobalSuppressions.cs -------------------------------------------------------------------------------- /Insight.Database.Providers.MySql/Insight.Database.Providers.MySql.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | An Insight.Database provider for MySql so the two can play together nicely. 5 | Automatically installs MySql.Data driver. 6 | 7 | Insight orm sql mysql micro-orm 8 | 9 | 10 | 11 | LibraryPackage 12 | 13 | 14 | 15 | net8.0;netstandard2.0;net48 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Insight.Database.Providers.MySql/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonwagner/Insight.Database/cf55c24cda26a2300f9270a5edcee6ebe473f9f8/Insight.Database.Providers.MySql/Properties/AssemblyInfo.cs -------------------------------------------------------------------------------- /Insight.Database.Providers.MySqlConnector/GlobalSuppressions.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonwagner/Insight.Database/cf55c24cda26a2300f9270a5edcee6ebe473f9f8/Insight.Database.Providers.MySqlConnector/GlobalSuppressions.cs -------------------------------------------------------------------------------- /Insight.Database.Providers.MySqlConnector/Insight.Database.Providers.MySqlConnector.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | An Insight.Database provider for MySQL so the two can play together nicely. 5 | Automatically installs MySqlConnector driver. 6 | 7 | Insight orm sql mysql micro-orm 8 | 9 | 10 | 11 | LibraryPackage 12 | 13 | 14 | 15 | net8.0;netstandard2.0;net48 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Insight.Database.Providers.MySqlConnector/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | [assembly: AssemblyCopyright("Copyright © Jon Wagner, used by permission")] 5 | [assembly: ComVisible(false)] 6 | -------------------------------------------------------------------------------- /Insight.Database.Providers.OracleManaged.Core/GlobalSuppressions.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonwagner/Insight.Database/cf55c24cda26a2300f9270a5edcee6ebe473f9f8/Insight.Database.Providers.OracleManaged.Core/GlobalSuppressions.cs -------------------------------------------------------------------------------- /Insight.Database.Providers.OracleManaged.Core/Insight.Database.Providers.OracleManaged.Core.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | An Oracle ODP.NET Managed Driver provider for Insight.Database so Insight.Database can do its magic on Oracle. 5 | Automatically installs Oracle ODP.NET Managed Driver. Depends on Insight.Database. 6 | 7 | Insight orm sql oracle managed micro-orm 8 | 9 | 10 | 11 | LibraryPackage 12 | net8.0;netstandard2.1;net48 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Insight.Database.Providers.OracleManaged.Core/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonwagner/Insight.Database/cf55c24cda26a2300f9270a5edcee6ebe473f9f8/Insight.Database.Providers.OracleManaged.Core/Properties/AssemblyInfo.cs -------------------------------------------------------------------------------- /Insight.Database.Providers.PostgreSQL/GlobalSuppressions.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonwagner/Insight.Database/cf55c24cda26a2300f9270a5edcee6ebe473f9f8/Insight.Database.Providers.PostgreSQL/GlobalSuppressions.cs -------------------------------------------------------------------------------- /Insight.Database.Providers.PostgreSQL/Insight.Database.Providers.PostgreSQL.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | A PostgreSQL provider for Insight.Database so Insight.Database can do its magic on PostgreSQL. 5 | Automatically installs Npgsql PostgreSQL driver. 6 | 7 | Insight orm sql postgre postgresql managed micro-orm 8 | 9 | 10 | 11 | LibraryPackage 12 | 13 | 14 | 15 | net8.0;netstandard2.0;net48 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Insight.Database.Providers.PostgreSQL/NpgsqlConnectionWithRecordsets.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Data.Common; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | using Npgsql; 10 | 11 | namespace Insight.Database.Providers.PostgreSQL 12 | { 13 | /// 14 | /// Opens a postgres connection that automatically deferences refcursors into recordsets. 15 | /// 16 | public class NpgsqlConnectionWithRecordsets : DbConnectionWrapper 17 | { 18 | /// 19 | /// Initializes a new instance of the NpgsqlConnectionWithRecordsets class. 20 | /// 21 | /// The connectionstring to use to connect to the database. 22 | public NpgsqlConnectionWithRecordsets(NpgsqlConnectionStringBuilder connectionString) : base(connectionString.Connection()) 23 | { 24 | } 25 | 26 | /// 27 | protected override DbCommand CreateDbCommand() 28 | { 29 | return new NpgsqlCommandWithRecordsets(this, InnerConnection.CreateCommand()); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Insight.Database.Providers.PostgreSQL/NpgsqlConnectionWithSchema.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Text.RegularExpressions; 7 | using System.Threading.Tasks; 8 | using Npgsql; 9 | 10 | namespace Insight.Database.Providers.PostgreSQL 11 | { 12 | /// 13 | /// A wrapped connection that automatically sets the schema when the connection is opened. 14 | /// 15 | public class NpgsqlConnectionWithSchema : DbConnectionWrapper 16 | { 17 | /// 18 | /// A regex defining a valild Postgres identifier. Note that it doesn't support quoted identifiers. 19 | /// 20 | private static string _validPostgresIdentifier = @"[\w\.\$_]+"; 21 | 22 | /// 23 | /// The sql to use to select the schema upon opening. 24 | /// 25 | private string _switchSchemaSql; 26 | 27 | /// 28 | /// Initializes a new instance of the NpgsqlConnectionWithSchema class. 29 | /// 30 | /// The connection string to wrap. 31 | /// The schema to select upon opening the connection. 32 | public NpgsqlConnectionWithSchema(NpgsqlConnectionStringBuilder connectionString, string schema) 33 | : base(connectionString.Connection()) 34 | { 35 | if (schema == null) 36 | throw new ArgumentNullException("schema"); 37 | if (!Regex.Match(schema, String.Format(CultureInfo.InvariantCulture, "^{0}(,{0})*$", _validPostgresIdentifier)).Success) 38 | throw new ArgumentException("Schema contained invalid characters", "schema"); 39 | 40 | Schema = schema; 41 | 42 | _switchSchemaSql = String.Format(CultureInfo.InvariantCulture, "SET SCHEMA '{0}'", Schema); 43 | } 44 | 45 | /// 46 | /// Gets the schema for this connection. 47 | /// 48 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")] 49 | public string Schema { get; private set; } 50 | 51 | /// 52 | public override void Open() 53 | { 54 | base.Open(); 55 | InnerConnection.ExecuteSql(_switchSchemaSql); 56 | } 57 | 58 | /// 59 | public async override Task OpenAsync(System.Threading.CancellationToken cancellationToken) 60 | { 61 | await base.OpenAsync(cancellationToken).ConfigureAwait(false); 62 | await InnerConnection.ExecuteSqlAsync(_switchSchemaSql, cancellationToken).ConfigureAwait(false); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Insight.Database.Providers.PostgreSQL/NpgsqlExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Npgsql; 8 | 9 | namespace Insight.Database.Providers.PostgreSQL 10 | { 11 | /// 12 | /// Extension methods for Postgres. 13 | /// 14 | public static class NpgsqlExtensions 15 | { 16 | /// 17 | /// Creates a connection wrapper that automatically sets the schema when the connection is opened. 18 | /// 19 | /// The connection string for the connection. 20 | /// The schema to select upon opening. 21 | /// A wrapped connection. 22 | public static IDbConnection ConnectionWithSchema(this NpgsqlConnectionStringBuilder connectionString, string schema) 23 | { 24 | return new NpgsqlConnectionWithSchema(connectionString, schema); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Insight.Database.Providers.PostgreSQL/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonwagner/Insight.Database/cf55c24cda26a2300f9270a5edcee6ebe473f9f8/Insight.Database.Providers.PostgreSQL/Properties/AssemblyInfo.cs -------------------------------------------------------------------------------- /Insight.Database/Insight.Database.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0;netstandard2.0;net48 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Insight.Sample/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Insight.Sample/Beer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Insight.Database.Sample 8 | { 9 | class Beer 10 | { 11 | public int Id { get; set; } 12 | public string Name { get; set; } 13 | public string Flavor { get; set; } 14 | public decimal? OriginalGravity { get; set; } 15 | public string Details { get; set; } 16 | public int InStock { get; set; } 17 | public string Style { get; set; } 18 | 19 | public Beer() 20 | { 21 | } 22 | 23 | public Beer(string name) 24 | { 25 | Name = name; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Insight.Sample/Dictionary.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Insight.Database.Sample 7 | { 8 | class Dictionary 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Insight.Sample/Insight.Sample.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | exe 4 | net8.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Insight.Sample/InsightSample.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonwagner/Insight.Database/cf55c24cda26a2300f9270a5edcee6ebe473f9f8/Insight.Sample/InsightSample.sql -------------------------------------------------------------------------------- /Insight.Tests.Json/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Insight.Tests.Json/Insight.Tests.Json.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net8.0 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Insight.Tests.Json/JsonTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using NUnit.Framework; 7 | using NUnit.Framework.Legacy; 8 | using Insight.Database; 9 | using Insight.Database.Json; 10 | 11 | namespace Insight.Tests.Json 12 | { 13 | [TestFixture] 14 | public class JsonTests : BaseTest 15 | { 16 | [OneTimeSetUp] 17 | public static void OneTimeSetup() 18 | { 19 | TestSetup.CreateTestDatabase(); 20 | } 21 | 22 | public class JsonClass 23 | { 24 | [Column(SerializationMode = SerializationMode.Json)] 25 | public JsonSubClass SubClass; 26 | } 27 | 28 | public class JsonSubClass 29 | { 30 | public string Foo; 31 | public int Bar; 32 | } 33 | 34 | [Test] 35 | public void TestJsonNetOverride() 36 | { 37 | JsonNetObjectSerializer.Initialize(); 38 | 39 | using (var connection = ConnectionWithTransaction()) 40 | { 41 | connection.ExecuteSql("CREATE PROC ClassAsJsonNetParameter @SubClass [varchar](max) AS SELECT SubClass=@SubClass"); 42 | 43 | var input = new JsonClass(); 44 | input.SubClass = new JsonSubClass() { Foo = "foo", Bar = 5 }; 45 | 46 | ClassicAssert.AreEqual("{\"Foo\":\"foo\",\"Bar\":5}", connection.Single("ClassAsJsonNetParameter", input)); 47 | 48 | var result = connection.Query("ClassAsJsonNetParameter", input).First(); 49 | ClassicAssert.IsNotNull(result.SubClass); 50 | ClassicAssert.AreEqual(input.SubClass.Foo, result.SubClass.Foo); 51 | ClassicAssert.AreEqual(input.SubClass.Bar, result.SubClass.Bar); 52 | } 53 | } 54 | 55 | [Test] 56 | public void TestNullValue() 57 | { 58 | using (var connection = ConnectionWithTransaction()) 59 | { 60 | var result = connection.QuerySql("SELECT SubClass=CONVERT (varchar(MAX),NULL)").First(); 61 | ClassicAssert.IsNull(result.SubClass); 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Insight.Tests.MiniProfiler/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Insight.Tests.MiniProfiler/Insight.Tests.MiniProfiler.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net8.0 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Insight.Tests.MiniProfiler/MiniProfilerTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data.Common; 4 | using System.Data.SqlClient; 5 | using System.Linq; 6 | using System.Text; 7 | using Insight.Database; 8 | using Insight.Database.Providers.MiniProfiler; 9 | using NUnit.Framework; 10 | using NUnit.Framework.Legacy; 11 | using StackExchange.Profiling; 12 | using StackExchange.Profiling.Data; 13 | 14 | namespace Insight.Tests 15 | { 16 | /// 17 | /// Tests that the mini-profiler connection wrapper does not interfere with SQL parameter detection. 18 | /// 19 | [TestFixture] 20 | class MiniProfilerTests : BaseTest 21 | { 22 | [OneTimeSetUp] 23 | public static void OneTimeSetup() 24 | { 25 | TestSetup.CreateTestDatabase(); 26 | } 27 | 28 | [Test] 29 | public void TestProfiledSqlQuery() 30 | { 31 | var profiled = new ProfiledDbConnection((DbConnection)Connection(), MiniProfiler.Current); 32 | var result = profiled.QuerySql("SELECT @p --MiniProfiler", new { p = 1 }).First(); 33 | 34 | ClassicAssert.AreEqual((int)1, result); 35 | } 36 | 37 | /// 38 | /// Make sure that a profiled connection still can auto-detect the parameters. 39 | /// 40 | [Test] 41 | public void TestProfiledStoredProcWithParameters() 42 | { 43 | using (var connection = ConnectionWithTransaction()) 44 | { 45 | connection.ExecuteSql("CREATE PROC InsightTestProcMiniProfiler (@Value int = 5) AS SELECT Value=@Value"); 46 | 47 | var profiled = new ProfiledDbConnection((DbConnection)connection, MiniProfiler.Current); 48 | var result = profiled.Query("InsightTestProcMiniProfiler", new { Value = 1 }).First(); 49 | 50 | ClassicAssert.AreEqual((int)1, result); 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Insight.Tests.MsSqlClient/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Insight.Tests.MsSqlClient/AsyncQueryCoreTests.cs: -------------------------------------------------------------------------------- 1 | using Insight.Database; 2 | using Insight.Tests.MsSqlClient.Cases; 3 | using NUnit.Framework; 4 | using NUnit.Framework.Legacy; 5 | using System.Data; 6 | using System.Linq; 7 | 8 | namespace Insight.Tests.MsSqlClient 9 | { 10 | [TestFixture] 11 | public partial class AsyncQueryCoreTests : MsSqlClientBaseTest 12 | { 13 | #region Basic Tests 14 | 15 | [Test] 16 | public void TestAutoClose() 17 | { 18 | ConnectionStateCase.ForEach(c => 19 | { 20 | var result = c.QueryAsync(Beer.SelectAllProc).Result; 21 | Beer.VerifyAll(result); 22 | }); 23 | } 24 | 25 | [Test] 26 | public void TestForceClose() 27 | { 28 | ConnectionStateCase.ForEach(c => 29 | { 30 | bool wasOpen = c.State == ConnectionState.Open; 31 | 32 | c.QueryAsync(Beer.SelectAllProc, commandBehavior: CommandBehavior.CloseConnection).Wait(); 33 | 34 | ClassicAssert.AreEqual(ConnectionState.Closed, c.State); 35 | if (wasOpen) 36 | c.Open(); 37 | }); 38 | } 39 | 40 | [Test] 41 | public void TestOutputParameters() 42 | { 43 | var input = new InOutParameters { In = 5 }; 44 | var output = new OutParameters(); 45 | 46 | Connection().QueryAsync(InOutParameters.ProcName, input, outputParameters: output).Wait(); 47 | 48 | input.Verify(output); 49 | } 50 | 51 | #endregion Basic Tests 52 | 53 | [Test] 54 | public void TestChildrenWithOneToOne() 55 | { 56 | // child records that include a one-to-one mapping 57 | 58 | var result = Connection().QuerySqlAsync( 59 | Beer.GetSelectNestedChildren(1, 2), 60 | null, 61 | Query.Returns(Some.Records) 62 | .ThenChildren(OneToOne.Records)).Result; 63 | 64 | ClassicAssert.AreEqual(3, result.Count()); 65 | ClassicAssert.AreEqual(1, result[0].List.Count()); 66 | ClassicAssert.IsNotNull(result[0].List[0].More); 67 | result[0].List[0].More.Verify(); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Insight.Tests.MsSqlClient/Cases/ConnectionStateCase.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using NUnit.Framework.Legacy; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Data; 6 | using System.Data.SqlClient; 7 | 8 | namespace Insight.Tests.MsSqlClient.Cases 9 | { 10 | /// 11 | /// Tests all of the cases for managing connection state. 12 | /// 13 | internal class ConnectionStateCase : IDisposable 14 | { 15 | public bool IsOpen; 16 | 17 | public IDbConnection Connection { get; private set; } 18 | 19 | public ConnectionStateCase(bool open) 20 | { 21 | IsOpen = open; 22 | Connection = new SqlConnection(BaseTest.ConnectionString); 23 | if (IsOpen) 24 | Connection.Open(); 25 | } 26 | 27 | public void Dispose() 28 | { 29 | Connection.Dispose(); 30 | GC.SuppressFinalize(this); 31 | } 32 | 33 | public static void ForEach(Action test) 34 | { 35 | foreach (var c in new List() { new ConnectionStateCase(false), new ConnectionStateCase(true) }) 36 | using (c) 37 | { 38 | c.VerifyPreCondition(); 39 | test(c.Connection); 40 | c.VerifyPostCondition(); 41 | } 42 | } 43 | 44 | private void VerifyPreCondition() 45 | { 46 | ClassicAssert.AreEqual(IsOpen, Connection.State == ConnectionState.Open); 47 | } 48 | 49 | private void VerifyPostCondition() 50 | { 51 | ClassicAssert.AreEqual(IsOpen, Connection.State == ConnectionState.Open); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Insight.Tests.MsSqlClient/Cases/OutputParameters.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using NUnit.Framework.Legacy; 3 | 4 | #pragma warning disable 0649 5 | 6 | namespace Insight.Tests.MsSqlClient.Cases 7 | { 8 | /// 9 | /// Standard In/Out parameters structure. 10 | /// 11 | internal class InOutParameters 12 | { 13 | public int In; 14 | public int Out1; 15 | 16 | public const string ProcName = "ProcWithOutputParameters"; 17 | 18 | public void Verify(OutParameters output) 19 | { 20 | ClassicAssert.AreEqual(In, Out1); 21 | output.Verify(In); 22 | } 23 | } 24 | 25 | /// 26 | /// Standard output parameters structure. 27 | /// 28 | internal class OutParameters 29 | { 30 | public int Out2; 31 | public int Return_Value; 32 | 33 | public void Verify(int input) 34 | { 35 | ClassicAssert.AreEqual(input, Out2); 36 | ClassicAssert.AreEqual(input, Return_Value); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Insight.Tests.MsSqlClient/Insight.Tests.MsSqlClient.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | 5 | 6 | 7 | 8 | $(DefineConstants);NO_SQL_TYPES 9 | NU1701 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Insight.Tests.MsSqlClient/MsSqlClientBaseTest.cs: -------------------------------------------------------------------------------- 1 | using Insight.Database; 2 | using Microsoft.Data.SqlClient; 3 | using NUnit.Framework; 4 | using System.Data; 5 | 6 | namespace Insight.Tests.MsSqlClient 7 | { 8 | public class MsSqlClientBaseTest 9 | { 10 | public IDbConnection Connection() 11 | { 12 | return new SqlConnection(BaseTest.ConnectionString); 13 | } 14 | 15 | public IDbConnection ConnectionWithTransaction() 16 | { 17 | return new SqlConnection(BaseTest.ConnectionString).OpenWithTransaction(); 18 | } 19 | } 20 | 21 | [SetUpFixture] 22 | public class MsSqlTestSetup 23 | { 24 | [OneTimeSetUp] 25 | public void OneTimeSetup() 26 | { 27 | TestSetup.CreateTestDatabase(); 28 | } 29 | 30 | [OneTimeTearDown] 31 | public void OneTimeTeardown() 32 | { 33 | TestSetup.TeardownFixture(); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Insight.Tests.MsSqlClient/SqlServerTests.cs: -------------------------------------------------------------------------------- 1 | using Insight.Database; 2 | using Insight.Tests.MsSqlClient.Cases; 3 | using NUnit.Framework; 4 | using NUnit.Framework.Legacy; 5 | using System.Data; 6 | using System.Linq; 7 | 8 | namespace Insight.Tests.MsSqlClient 9 | { 10 | [TestFixture] 11 | public partial class SqlServerTests : MsSqlClientBaseTest 12 | { 13 | public class SprocMetadata 14 | { 15 | public string Name; 16 | } 17 | 18 | [Test] 19 | public void TestSysProcs() 20 | { 21 | // Issue #498 22 | var results = Connection().QuerySql("SELECT * FROM sys.objects"); 23 | ClassicAssert.Greater(results.Count, 0); 24 | ClassicAssert.IsNotNull(results[0].Name); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Insight.Tests.MySql/Insight.Tests.MySql.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net8.0 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Insight.Tests.MySql/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Insight.Tests.MySqlConnector/Insight.Tests.MySqlConnector.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net8.0 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Insight.Tests.OracleManaged.Core/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 |
6 | 7 | 8 | 9 | 10 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Insight.Tests.OracleManaged.Core/Insight.Tests.OracleManaged.Core.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net8.0 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Insight.Tests.PostgreSQL/Insight.Tests.PostgreSQL.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net8.0 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Insight.Tests.SQLite/Insight.Tests.SQLite.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net8.0 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Insight.Tests.SQLite/SQLiteTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using NUnit.Framework.Legacy; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using Insight.Database; 9 | using Microsoft.Data.Sqlite; 10 | using System.IO; 11 | 12 | namespace Insight.Tests.SQLite 13 | { 14 | [TestFixture] 15 | public class SQLiteTests 16 | { 17 | private static string _filename = "testdb.sqlite"; 18 | private SqliteConnectionStringBuilder _builder; 19 | private SqliteConnection _connection; 20 | 21 | [SetUp] 22 | public void SetUp() 23 | { 24 | _builder = new SqliteConnectionStringBuilder() { DataSource = _filename }; 25 | _connection = new SqliteConnection(_builder.ConnectionString); 26 | } 27 | 28 | [TearDown] 29 | public void TearDown() 30 | { 31 | try 32 | { 33 | File.Delete(_filename); 34 | } 35 | catch { } 36 | } 37 | 38 | public class Beer 39 | { 40 | public int id; 41 | public string name; 42 | } 43 | 44 | [Test] 45 | public void BasicTests() 46 | { 47 | var beer = new Beer() { id = 1, name = "Troeg's Mad Elf" }; 48 | 49 | _connection.ExecuteSql("CREATE TABLE Beer (id int, name varchar(400))"); 50 | _connection.ExecuteSql("INSERT INTO Beer (id, name) values (@id, @name)", beer); 51 | 52 | var pour = _connection.QuerySql("SELECT * FROM Beer WHERE ID = @id", new { id = 1 }).FirstOrDefault(); 53 | ClassicAssert.IsNotNull(pour); 54 | ClassicAssert.AreEqual(beer.id, pour.id); 55 | ClassicAssert.AreEqual(beer.name, pour.name); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Insight.Tests.SQLite/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Insight.Tests/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Insight.Tests/AsyncExecuteScalarTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Data.SqlClient; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using Insight.Database; 9 | using Insight.Tests.Cases; 10 | using NUnit.Framework; 11 | using NUnit.Framework.Legacy; 12 | 13 | namespace Insight.Tests 14 | { 15 | [TestFixture] 16 | public class AsyncExecuteScalarTests : BaseTest 17 | { 18 | [Test] 19 | public void TestAutoClose() 20 | { 21 | ConnectionStateCase.ForEach(c => 22 | { 23 | var parameters = new { p = 1 }; 24 | var result = c.ExecuteScalarSqlAsync("SELECT @p", parameters).Result; 25 | ClassicAssert.AreEqual(parameters.p, result); 26 | }); 27 | } 28 | 29 | [Test] 30 | public void TestForceClose() 31 | { 32 | ConnectionStateCase.ForEach(c => 33 | { 34 | bool wasOpen = c.State == ConnectionState.Open; 35 | var recordCount = c.ExecuteScalarSqlAsync("SELECT @p", new { p = 1 }, closeConnection: true).Result; 36 | ClassicAssert.AreEqual(ConnectionState.Closed, c.State); 37 | if (wasOpen) 38 | c.Open(); 39 | }); 40 | } 41 | 42 | [Test] 43 | public void TestOutputParameters() 44 | { 45 | var input = new InOutParameters { In = 5 }; 46 | var output = new OutParameters(); 47 | 48 | var result = Connection().ExecuteScalarAsync(InOutParameters.ProcName, input, outputParameters: output).Result; 49 | 50 | ClassicAssert.AreEqual(input.In, result); 51 | input.Verify(output); 52 | } 53 | 54 | [Test] 55 | public void TestNullableReturn() 56 | { 57 | var result = Connection().ExecuteScalarSqlAsync("SELECT CAST(NULL as INT)").Result; 58 | 59 | ClassicAssert.AreEqual(null, result); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Insight.Tests/AsyncExecuteTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Data.SqlClient; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using Insight.Database; 9 | using Insight.Tests.Cases; 10 | using NUnit.Framework; 11 | using NUnit.Framework.Legacy; 12 | 13 | namespace Insight.Tests 14 | { 15 | [TestFixture] 16 | public class AsyncExecuteTests : BaseTest 17 | { 18 | [Test] 19 | public void TestAutoClose() 20 | { 21 | ConnectionStateCase.ForEach(c => 22 | { 23 | var recordCount = c.ExecuteSqlAsync("SELECT @p", new { p = 1 }).Result; 24 | 25 | ClassicAssert.AreEqual(-1, recordCount); 26 | }); 27 | } 28 | 29 | [Test] 30 | public void TestForceClose() 31 | { 32 | ConnectionStateCase.ForEach(c => 33 | { 34 | bool wasOpen = c.State == ConnectionState.Open; 35 | var recordCount = c.ExecuteSqlAsync("SELECT @p", new { p = 1 }, closeConnection: true).Result; 36 | 37 | ClassicAssert.AreEqual(ConnectionState.Closed, c.State); 38 | if (wasOpen) 39 | c.Open(); 40 | }); 41 | } 42 | 43 | [Test] 44 | public void TestOutputParameters() 45 | { 46 | var input = new InOutParameters { In = 5 }; 47 | var output = new OutParameters(); 48 | 49 | Connection().ExecuteAsync(InOutParameters.ProcName, input, outputParameters: output).Wait(); 50 | 51 | input.Verify(output); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Insight.Tests/AsyncInsertTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Data.SqlClient; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using Insight.Database; 9 | using Insight.Tests.Cases; 10 | using NUnit.Framework; 11 | using NUnit.Framework.Legacy; 12 | 13 | namespace Insight.Tests 14 | { 15 | [TestFixture] 16 | public class AsyncInsertTests : BaseTest 17 | { 18 | #region Base Cases 19 | [Test] 20 | public void TestAutoClose() 21 | { 22 | ConnectionStateCase.ForEach(c => 23 | { 24 | var input = new InOutParameters { In = 5 }; 25 | var result = c.InsertSqlAsync("SELECT @In", input).Result; 26 | }); 27 | } 28 | 29 | [Test] 30 | public void TestForceClose() 31 | { 32 | ConnectionStateCase.ForEach(c => 33 | { 34 | bool wasOpen = c.State == ConnectionState.Open; 35 | 36 | var input = new InOutParameters { In = 5 }; 37 | var recordCount = c.InsertSqlAsync("SELECT @In", input, commandBehavior: CommandBehavior.CloseConnection).Result; 38 | 39 | ClassicAssert.AreEqual(ConnectionState.Closed, c.State); 40 | if (wasOpen) 41 | c.Open(); 42 | }); 43 | } 44 | 45 | [Test] 46 | public void TestOutputParameters() 47 | { 48 | var input = new InOutParameters { In = 5 }; 49 | var output = new OutParameters(); 50 | 51 | var result = Connection().InsertAsync(InOutParameters.ProcName, input, outputParameters: output).Result; 52 | 53 | input.Verify(output); 54 | } 55 | 56 | [Test] 57 | public void TestOutputParametersInList() 58 | { 59 | var list = new List(); 60 | var input = new InOutParameters { In = 5 }; 61 | var output = new OutParameters(); 62 | 63 | var result = Connection().InsertListAsync(InOutParameters.ProcName, list, input, outputParameters: output).Result; 64 | 65 | input.Verify(output); 66 | } 67 | #endregion 68 | 69 | [Test] 70 | public void TestInsertWithIDReturn() 71 | { 72 | using (var c = Connection().OpenWithTransaction()) 73 | { 74 | var beer = Beer.GetSample(); 75 | c.InsertAsync(Beer.InsertProc, beer).Wait(); 76 | beer.VerifySample(); 77 | } 78 | } 79 | 80 | [Test] 81 | public void TestInsertListWithIDReturn() 82 | { 83 | using (var c = Connection().OpenWithTransaction()) 84 | { 85 | var list = new List(); 86 | list.Add(Beer.GetSample()); 87 | list.Add(Beer.GetSample()); 88 | 89 | c.InsertListAsync(Beer.InsertManyProc, list).Wait(); 90 | 91 | foreach (var beer in list) 92 | beer.VerifySample(); 93 | } 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Insight.Tests/AsyncQueryCoreTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Data.SqlClient; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using Insight.Database; 9 | using Insight.Tests.Cases; 10 | using NUnit.Framework; 11 | using NUnit.Framework.Legacy; 12 | 13 | namespace Insight.Tests 14 | { 15 | [TestFixture] 16 | public partial class AsyncQueryCoreTests : BaseTest 17 | { 18 | #region Basic Tests 19 | [Test] 20 | public void TestAutoClose() 21 | { 22 | ConnectionStateCase.ForEach(c => 23 | { 24 | var result = c.QueryAsync(Beer.SelectAllProc).Result; 25 | Beer.VerifyAll(result); 26 | }); 27 | } 28 | 29 | [Test] 30 | public void TestForceClose() 31 | { 32 | ConnectionStateCase.ForEach(c => 33 | { 34 | bool wasOpen = c.State == ConnectionState.Open; 35 | 36 | c.QueryAsync(Beer.SelectAllProc, commandBehavior: CommandBehavior.CloseConnection).Wait(); 37 | 38 | ClassicAssert.AreEqual(ConnectionState.Closed, c.State); 39 | if (wasOpen) 40 | c.Open(); 41 | }); 42 | } 43 | 44 | [Test] 45 | public void TestOutputParameters() 46 | { 47 | var input = new InOutParameters { In = 5 }; 48 | var output = new OutParameters(); 49 | 50 | Connection().QueryAsync(InOutParameters.ProcName, input, outputParameters: output).Wait(); 51 | 52 | input.Verify(output); 53 | } 54 | #endregion 55 | 56 | [Test] 57 | public void TestChildrenWithOneToOne() 58 | { 59 | // child records that include a one-to-one mapping 60 | 61 | var result = Connection().QuerySqlAsync( 62 | Beer.GetSelectNestedChildren(1, 2), 63 | null, 64 | Query.Returns(Some.Records) 65 | .ThenChildren(OneToOne.Records)).Result; 66 | 67 | ClassicAssert.AreEqual(3, result.Count()); 68 | ClassicAssert.AreEqual(1, result[0].List.Count()); 69 | ClassicAssert.IsNotNull(result[0].List[0].More); 70 | result[0].List[0].More.Verify(); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Insight.Tests/AsyncQueryReaderTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Data.SqlClient; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using Insight.Database; 9 | using Insight.Tests.Cases; 10 | using NUnit.Framework; 11 | using NUnit.Framework.Legacy; 12 | namespace Insight.Tests 13 | { 14 | [TestFixture] 15 | public class AsyncQueryReaderTests : BaseTest 16 | { 17 | #region Base Tests 18 | [Test] 19 | public void TestAutoClose() 20 | { 21 | ConnectionStateCase.ForEach(c => 22 | { 23 | var result = c.QuerySqlAsync("SELECT @p", new { p = 1 }, reader => 1).Result; 24 | 25 | ClassicAssert.AreEqual(1, result); 26 | }); 27 | } 28 | 29 | [Test] 30 | public void TestForceClose() 31 | { 32 | ConnectionStateCase.ForEach(c => 33 | { 34 | bool wasOpen = c.State == ConnectionState.Open; 35 | 36 | var result = c.QuerySqlAsync("SELECT @p", new { p = 1 }, reader => 1, commandBehavior: CommandBehavior.CloseConnection).Result; 37 | 38 | ClassicAssert.AreEqual(ConnectionState.Closed, c.State); 39 | if (wasOpen) 40 | c.Open(); 41 | }); 42 | } 43 | 44 | [Test] 45 | public void TestOutputParameters() 46 | { 47 | var input = new InOutParameters { In = 5 }; 48 | var output = new OutParameters(); 49 | 50 | // notice here how we don't actually read the recordset, but the framework goes all the way to the end for us 51 | Connection().QueryAsync(InOutParameters.ProcName, input, reader => 1, outputParameters: output).Wait(); 52 | 53 | input.Verify(output); 54 | } 55 | #endregion 56 | 57 | [Test] 58 | public void TestReadWithoutResults() 59 | { 60 | IList results = null; 61 | Connection().QueryAsync(Beer.SelectAllProc, null, reader => { results = reader.ToList(); }).Wait(); 62 | 63 | Beer.VerifyAll(results); 64 | } 65 | 66 | [Test] 67 | public void TestReadList() 68 | { 69 | var results = Connection().QueryAsync(Beer.SelectAllProc, null, reader => reader.ToList()).Result; 70 | 71 | Beer.VerifyAll(results); 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /Insight.Tests/Cases/ConnectionStateCase.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using NUnit.Framework.Legacy; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Data; 6 | using System.Data.SqlClient; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace Insight.Tests.Cases 12 | { 13 | /// 14 | /// Tests all of the cases for managing connection state. 15 | /// 16 | class ConnectionStateCase : IDisposable 17 | { 18 | public bool IsOpen; 19 | 20 | public IDbConnection Connection { get; private set; } 21 | 22 | public ConnectionStateCase(bool open) 23 | { 24 | IsOpen = open; 25 | Connection = new SqlConnection(BaseTest.ConnectionString); 26 | if (IsOpen) 27 | Connection.Open(); 28 | } 29 | 30 | public void Dispose() 31 | { 32 | Connection.Dispose(); 33 | GC.SuppressFinalize(this); 34 | } 35 | 36 | public static void ForEach(Action test) 37 | { 38 | foreach (var c in new List() { new ConnectionStateCase(false), new ConnectionStateCase(true) }) 39 | using (c) 40 | { 41 | c.VerifyPreCondition(); 42 | test(c.Connection); 43 | c.VerifyPostCondition(); 44 | } 45 | } 46 | 47 | private void VerifyPreCondition() 48 | { 49 | ClassicAssert.AreEqual(IsOpen, Connection.State == ConnectionState.Open); 50 | } 51 | 52 | private void VerifyPostCondition() 53 | { 54 | ClassicAssert.AreEqual(IsOpen, Connection.State == ConnectionState.Open); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Insight.Tests/Cases/OutputParameters.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using NUnit.Framework.Legacy; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Insight.Tests.Cases 10 | { 11 | /// 12 | /// Standard In/Out parameters structure. 13 | /// 14 | class InOutParameters 15 | { 16 | public int In; 17 | public int Out1; 18 | 19 | public const string ProcName = "ProcWithOutputParameters"; 20 | 21 | public void Verify(OutParameters output) 22 | { 23 | ClassicAssert.AreEqual(In, Out1); 24 | output.Verify(In); 25 | } 26 | } 27 | 28 | /// 29 | /// Standard output parameters structure. 30 | /// 31 | class OutParameters 32 | { 33 | public int Out2; 34 | public int Return_Value; 35 | 36 | public void Verify(int input) 37 | { 38 | ClassicAssert.AreEqual(input, Out2); 39 | ClassicAssert.AreEqual(input, Return_Value); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Insight.Tests/ColumnAttributeTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Insight.Database; 7 | using NUnit.Framework; 8 | using NUnit.Framework.Legacy; 9 | 10 | #pragma warning disable 0649 11 | 12 | namespace Insight.Tests 13 | { 14 | /// 15 | /// Tests the ColumnAttribute overrides. 16 | /// 17 | class ColumnAttributeTests : BaseTest 18 | { 19 | class Data 20 | { 21 | public int Column; 22 | [Column("Column")] 23 | public int ColumnA; 24 | 25 | public int OtherProperty { get; set; } 26 | [Column("OtherProperty")] 27 | public int Property { get; set; } 28 | } 29 | 30 | [Test] 31 | public void ColumnMappingFillsInField() 32 | { 33 | Data data = Connection().QuerySql("SELECT [Column]=4").First(); 34 | 35 | ClassicAssert.AreEqual(4, data.ColumnA); 36 | ClassicAssert.AreEqual(0, data.Column); 37 | } 38 | 39 | [Test] 40 | public void ColumnMappingFillsInProperty() 41 | { 42 | Data data = Connection().QuerySql("SELECT [OtherProperty]=4").First(); 43 | 44 | ClassicAssert.AreEqual(4, data.Property); 45 | ClassicAssert.AreEqual(0, data.OtherProperty); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Insight.Tests/ConnectionStringSettingsTests.cs: -------------------------------------------------------------------------------- 1 | #if !NO_CONNECTION_SETTINGS 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Data; 5 | using System.Dynamic; 6 | using System.Linq; 7 | using System.Text; 8 | using Insight.Database; 9 | using NUnit.Framework; 10 | using System.Configuration; 11 | 12 | namespace Insight.Tests 13 | { 14 | /// 15 | /// Tests the behavior of the ConnectionStringSettings extensions. 16 | /// 17 | [TestFixture] 18 | public class ConnectionStringSettingsTests 19 | { 20 | [Test] 21 | public void ConnectionShouldThrowArgumentNullExceptionOnNull() 22 | { 23 | ConnectionStringSettings settings = null; 24 | 25 | Assert.Throws(() => settings.Connection()); 26 | } 27 | 28 | [Test] 29 | public void OpenShouldThrowArgumentNullExceptionOnNull() 30 | { 31 | ConnectionStringSettings settings = null; 32 | 33 | Assert.Throws(() => settings.Open()); 34 | } 35 | } 36 | } 37 | #endif 38 | -------------------------------------------------------------------------------- /Insight.Tests/ExpandoTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Dynamic; 5 | using System.Linq; 6 | using System.Text; 7 | using Insight.Database; 8 | using NUnit.Framework; 9 | using NUnit.Framework.Legacy; 10 | #if !NO_CONNECTION_SETTINGS 11 | using System.Configuration; 12 | #endif 13 | 14 | namespace Insight.Tests 15 | { 16 | /// 17 | /// Tests the behavior of the ConnectionStringSettings extensions. 18 | /// 19 | [TestFixture] 20 | public class ExpandoTests 21 | { 22 | [Test] 23 | public void TransformShouldCreateANewObject() 24 | { 25 | FastExpando o = new FastExpando(); 26 | dynamic d = o; 27 | d["A"] = "foo"; 28 | d["B"] = "goo"; 29 | 30 | FastExpando other = o.Transform(new Dictionary() { { "A", "Z" } }); 31 | IDictionary otherDict = other; 32 | dynamic otherD = other; 33 | 34 | ClassicAssert.AreNotSame(o, other); 35 | ClassicAssert.IsFalse(otherDict.ContainsKey("A")); 36 | ClassicAssert.IsNotNull(otherD["B"]); 37 | ClassicAssert.AreEqual(otherD["Z"], d["A"]); 38 | } 39 | 40 | [Test] 41 | public void TransformSucceedsWithExtraMap() 42 | { 43 | FastExpando o = new FastExpando(); 44 | 45 | FastExpando other = o.Transform(new Dictionary() { { "A", "Z" } }); 46 | IDictionary otherDict = other; 47 | 48 | ClassicAssert.AreNotSame(o, other); 49 | ClassicAssert.IsFalse(otherDict.ContainsKey("A")); 50 | ClassicAssert.IsFalse(otherDict.ContainsKey("Z")); 51 | } 52 | 53 | [Test] 54 | public void MutateShouldModifyObject() 55 | { 56 | FastExpando o = new FastExpando(); 57 | dynamic d = o; 58 | d["A"] = "foo"; 59 | d["B"] = "goo"; 60 | 61 | o.Mutate(new Dictionary() { { "A", "Z" } }); 62 | IDictionary dict = o; 63 | 64 | ClassicAssert.IsFalse(dict.ContainsKey("A")); 65 | ClassicAssert.IsNotNull(d["B"]); 66 | ClassicAssert.AreEqual(d["Z"], "foo"); 67 | } 68 | 69 | [Test] 70 | public void MutateSucceedsWithExtraMap() 71 | { 72 | FastExpando o = new FastExpando(); 73 | dynamic d = o; 74 | d["B"] = "goo"; 75 | 76 | o.Mutate(new Dictionary() { { "A", "Z" } }); 77 | IDictionary dict = o; 78 | 79 | ClassicAssert.IsFalse(dict.ContainsKey("A")); 80 | ClassicAssert.IsTrue(dict.ContainsKey("B")); 81 | } 82 | 83 | [Test] 84 | public void ExpandoShouldPreserveCase() 85 | { 86 | string key = "UlUl"; 87 | 88 | FastExpando o = new FastExpando(); 89 | var d = (IDictionary)o; 90 | o[key] = "goo"; 91 | 92 | ClassicAssert.AreEqual(key, d.Keys.First()); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Insight.Tests/GenericTypes.tt: -------------------------------------------------------------------------------- 1 | <#+ 2 | /// 3 | /// Helper constants for T4 templates for generic types. 4 | /// 5 | 6 | private const int GenericTypeMax = 16; 7 | private const string GenericTypeFormat = "T{0}"; 8 | private const string GenericArgumentFormat = "T{0} arg{0}"; 9 | private const string GenericParameterFormat = "arg{0}"; 10 | 11 | private static readonly string[] ordinalNumbers = new[] { "first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eighth", "nineth", "tenth", "eleventh", "twelfth", "thirteenth", "fourteenth", "fifteenth", "sixteenth" }; 12 | private static readonly string[] wordNumbers = new[] { "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen" }; 13 | 14 | public static string ConvertToWord(int number) 15 | { 16 | return wordNumbers[number - 1]; 17 | } 18 | 19 | public static string ConvertToOrdinal(int number) 20 | { 21 | return ordinalNumbers[number - 1]; 22 | } 23 | 24 | private static string GetGenericList(int typeCount, string format, string separator = ", ") 25 | { 26 | return string.Join(separator, Enumerable.Range(1, typeCount).Select(n => string.Format(format, n)).ToArray()); 27 | } 28 | #> -------------------------------------------------------------------------------- /Insight.Tests/Insight.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net8.0 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | TextTemplatingFileGenerator 39 | 40 | 41 | 42 | 43 | 44 | TextTemplatingFileGenerator 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /Insight.Tests/OdbcTests.cs: -------------------------------------------------------------------------------- 1 | #if !NO_ODBC 2 | using Insight.Database; 3 | using NUnit.Framework; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Configuration; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using Insight.Database.Providers.OleDb; 11 | 12 | namespace Insight.Tests 13 | { 14 | [TestFixture] 15 | public class OdbcTests : BaseTest 16 | { 17 | [OneTimeSetUp] 18 | public void SetUp() 19 | { 20 | ConnectionString = String.Format("Driver={{SQL Server}}; Server={0}; {1};", 21 | TestHost ?? ".", 22 | (Password != null) ? String.Format("Uid=sa; Pwd={0}", Password) : "Trusted_Connection=Yes"); 23 | } 24 | 25 | [Test] 26 | public void NamedParametersAreConvertedToPositionalParameters() 27 | { 28 | var c = new System.Data.Odbc.OdbcConnection(ConnectionString); 29 | dynamic i = c.QuerySql("SELECT p=@p, q=@q, r=@p", new { p = 5, q = 9 }).First(); 30 | ClassicAssert.AreEqual(5, i.p); 31 | ClassicAssert.AreEqual(9, i.q); 32 | ClassicAssert.AreEqual(5, i.r); 33 | } 34 | } 35 | } 36 | #endif 37 | -------------------------------------------------------------------------------- /Insight.Tests/OleDbTest.cs: -------------------------------------------------------------------------------- 1 | #if !NO_OLEDB 2 | using Insight.Database; 3 | using NUnit.Framework; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Configuration; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using Insight.Database.Providers.OleDb; 11 | 12 | namespace Insight.Tests 13 | { 14 | [TestFixture] 15 | public class OleDbTests : BaseTest 16 | { 17 | [OneTimeSetUp] 18 | public void SetUp() 19 | { 20 | } 21 | 22 | [Test] 23 | public void NamedParametersAreConvertedToPositionalParameters() 24 | { 25 | var c = new System.Data.OleDb.OleDbConnection( 26 | String.Format("Provider=MSOLEDBSQL;Server={0};Uid={1};Pwd={2};Trusted_Connection=no", 27 | TestHost, "sa", Password)); 28 | dynamic i = c.QuerySql("SELECT p=@p, q=@q, r=@p", new { p = 5, q = 9 }).First(); 29 | ClassicAssert.AreEqual(5, i.p); 30 | ClassicAssert.AreEqual(9, i.q); 31 | ClassicAssert.AreEqual(5, i.r); 32 | } 33 | } 34 | } 35 | #endif 36 | -------------------------------------------------------------------------------- /Insight.Tests/OptimisticTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Insight.Database; 7 | using NUnit.Framework; 8 | using NUnit.Framework.Legacy; 9 | using System.Data.Common; 10 | 11 | namespace Insight.Tests 12 | { 13 | [TestFixture] 14 | public class OptimisticTests : BaseTest 15 | { 16 | [Test] 17 | public void OptimisticConnectionCanCallProc() 18 | { 19 | var opt = new OptimisticConnection((DbConnection)Connection()); 20 | opt.Execute("sp_who"); 21 | } 22 | 23 | [Test] 24 | public void OptimisticExceptionsAreTranslated() 25 | { 26 | var opt = new OptimisticConnection((DbConnection)Connection()); 27 | Assert.Throws(() => opt.ExecuteSql("RAISERROR('(CONCURRENCY CHECK)', 16, 1)")); 28 | } 29 | 30 | [Test] 31 | public void OptimisticExceptionsAreTranslatedAsync() 32 | { 33 | var opt = new OptimisticConnection((DbConnection)Connection()); 34 | try 35 | { 36 | opt.ExecuteSqlAsync("RAISERROR('(CONCURRENCY CHECK)', 16, 1)").Wait(); 37 | } 38 | catch (AggregateException ax) 39 | { 40 | ClassicAssert.IsNotNull(ax.Flatten().InnerExceptions.OfType()); 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Insight.Tests/RegressionTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using NUnit.Framework; 7 | using NUnit.Framework.Legacy; 8 | using Insight.Database; 9 | 10 | namespace Insight.Tests 11 | { 12 | [TestFixture] 13 | public class RegressionTests : BaseTest 14 | { 15 | #region Git Issue #18 16 | class Beer 17 | { 18 | public int Id { get; set; } 19 | public string Name { get; set; } 20 | public int? AlcoholPts { get; set; } 21 | } 22 | 23 | [Test] 24 | public void TestIssue18() 25 | { 26 | using (var connection = ConnectionWithTransaction()) 27 | { 28 | connection.ExecuteSql("CREATE TABLE Beer18 (id int identity, name varchar(256), alcoholpts int)"); 29 | connection.ExecuteSql(@" 30 | CREATE PROC InsertBeer18 @id int, @name varchar(256), @alcoholpts [int] AS 31 | INSERT INTO Beer18 (Name, AlcoholPts) 32 | OUTPUT Inserted.Id 33 | VALUES (@Name, @AlcoholPts) 34 | "); 35 | 36 | Beer b = new Beer() { AlcoholPts = 11 }; 37 | connection.ExecuteScalar("InsertBeer18", b); 38 | ClassicAssert.AreEqual(11, connection.ExecuteScalarSql("SELECT AlcoholPts FROM Beer18")); 39 | } 40 | } 41 | #endregion 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Insight.Tests/SqlServerTypes/readme.htm: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Microsoft.SqlServer.Types 5 | 17 | 18 | 19 |
20 | ***This information may be out of date.*** 21 | 22 |

Action required to load native assemblies

23 |

24 | To deploy an application that uses spatial data types to a machine that does not have 'System CLR Types for SQL Server' installed you also need to deploy the native assembly SqlServerSpatial140.dll. Both x86 (32 bit) and x64 (64 bit) versions of this assembly have been added to your project under the SqlServerTypes\x86 and SqlServerTypes\x64 subdirectories. The native assembly msvcr120.dll is also included in case the C++ runtime is not installed. 25 |

26 |

27 | You need to add code to load the correct one of these assemblies at runtime (depending on the current architecture). 28 |

29 |

ASP.NET Web Sites

30 |

31 | For ASP.NET Web Sites, add the following block of code to the code behind file of the Web Form where you have added Report Viewer Control: 32 |

33 |     Default.aspx.cs:
34 |         
35 |     public partial class _Default : System.Web.UI.Page
36 |     {
37 |         static bool _isSqlTypesLoaded = false;
38 | 
39 |         public _Default()
40 |         {
41 |             if (!_isSqlTypesLoaded)
42 |             {
43 |                 SqlServerTypes.Utilities.LoadNativeAssemblies(Server.MapPath("~"));
44 |                 _isSqlTypesLoaded = true;
45 |             }
46 |             
47 |         }
48 |     }
49 | 
50 |

51 |

ASP.NET Web Applications

52 |

53 | For ASP.NET Web Applications, add the following line of code to the Application_Start method in Global.asax.cs: 54 |

    SqlServerTypes.Utilities.LoadNativeAssemblies(Server.MapPath("~/bin"));
55 |

56 |

Desktop Applications

57 |

58 | For desktop applications, add the following line of code to run before any spatial operations are performed: 59 |

    SqlServerTypes.Utilities.LoadNativeAssemblies(AppDomain.CurrentDomain.BaseDirectory);
60 |

61 |
62 | 63 | -------------------------------------------------------------------------------- /Insight.Tests/SyncExecuteScalarTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Data.SqlClient; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using Insight.Database; 9 | using Insight.Tests.Cases; 10 | using NUnit.Framework; 11 | using NUnit.Framework.Legacy; 12 | 13 | namespace Insight.Tests 14 | { 15 | [TestFixture] 16 | public class SyncExecuteScalarTests : BaseTest 17 | { 18 | [Test] 19 | public void TestAutoClose() 20 | { 21 | ConnectionStateCase.ForEach(c => 22 | { 23 | var parameters = new { p = 1 }; 24 | var result = c.ExecuteScalarSql("SELECT @p", parameters); 25 | ClassicAssert.AreEqual(parameters.p, result); 26 | }); 27 | } 28 | 29 | [Test] 30 | public void TestForceClose() 31 | { 32 | ConnectionStateCase.ForEach(c => 33 | { 34 | bool wasOpen = c.State == ConnectionState.Open; 35 | var recordCount = c.ExecuteScalarSql("SELECT @p", new { p = 1 }, closeConnection: true); 36 | ClassicAssert.AreEqual(ConnectionState.Closed, c.State); 37 | if (wasOpen) 38 | c.Open(); 39 | }); 40 | } 41 | 42 | [Test] 43 | public void TestOutputParameters() 44 | { 45 | var input = new InOutParameters { In = 5 }; 46 | var output = new OutParameters(); 47 | 48 | var result = Connection().ExecuteScalar(InOutParameters.ProcName, input, outputParameters: output); 49 | 50 | ClassicAssert.AreEqual(input.In, result); 51 | input.Verify(output); 52 | } 53 | 54 | [Test] 55 | public void NonNullableReturnShouldThrowWhenNoRowsAreReturned() 56 | { 57 | Assert.Throws(() => Connection().ExecuteScalarSql("PRINT 1")); 58 | } 59 | 60 | [Test] 61 | public void TestNullableReturn() 62 | { 63 | var result = Connection().ExecuteScalarSql("SELECT CAST(NULL as INT)"); 64 | 65 | ClassicAssert.AreEqual(null, result); 66 | } 67 | 68 | [Test] 69 | public void TestNullableStringReturnWhenNoRowsAreReturned() 70 | { 71 | var result = Connection().ExecuteScalarSql("PRINT 1"); 72 | 73 | ClassicAssert.AreEqual(null, result); 74 | } 75 | 76 | [Test] 77 | public void Given_a_null_result_When_querying_for_a_scalar_int_Then_the_result_is_not_silently_converted() 78 | { 79 | using (var connection = ConnectionWithTransaction()) 80 | { 81 | TestDelegate act = () => connection.ExecuteScalarSql("SELECT NULL"); 82 | 83 | Assert.That(act, Throws.Exception); 84 | } 85 | } 86 | 87 | [Test] 88 | public void Given_a_null_result_When_expecting_an_int_Then_the_result_is_not_silently_converted() 89 | { 90 | using (var connection = ConnectionWithTransaction()) 91 | { 92 | TestDelegate act = () => connection.QuerySql("SELECT NULL").Single(); 93 | 94 | Assert.That(act, Throws.Exception); 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Insight.Tests/SyncExecuteTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Data.SqlClient; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using Insight.Database; 9 | using Insight.Tests.Cases; 10 | using NUnit.Framework; 11 | using NUnit.Framework.Legacy; 12 | 13 | namespace Insight.Tests 14 | { 15 | [TestFixture] 16 | public class SyncExecuteTests : BaseTest 17 | { 18 | [Test] 19 | public void TestAutoClose() 20 | { 21 | ConnectionStateCase.ForEach(c => 22 | { 23 | var recordCount = c.ExecuteSql("SELECT @p", new { p = 1 }); 24 | 25 | ClassicAssert.AreEqual(-1, recordCount); 26 | }); 27 | } 28 | 29 | [Test] 30 | public void TestForceClose() 31 | { 32 | ConnectionStateCase.ForEach(c => 33 | { 34 | bool wasOpen = c.State == ConnectionState.Open; 35 | var recordCount = c.ExecuteSql("SELECT @p", new { p = 1 }, closeConnection: true); 36 | 37 | ClassicAssert.AreEqual(ConnectionState.Closed, c.State); 38 | if (wasOpen) 39 | c.Open(); 40 | }); 41 | } 42 | 43 | [Test] 44 | public void TestOutputParameters() 45 | { 46 | var input = new InOutParameters { In = 5 }; 47 | var output = new OutParameters(); 48 | 49 | Connection().Execute(InOutParameters.ProcName, input, outputParameters: output); 50 | 51 | input.Verify(output); 52 | } 53 | 54 | [Test] 55 | public void ForEachSqlBroken() 56 | { 57 | Connection().ForEachSql("select 1", Parameters.Empty, _ => { ; }); 58 | } 59 | 60 | [Test] 61 | public void ForEachBroken() 62 | { 63 | Connection().ForEach("sp_who", Parameters.Empty, _ => { ; }); 64 | } 65 | 66 | [Test] 67 | public void TestIssue174() 68 | { 69 | // parameter names should be case insensitive 70 | Connection().ExecuteSql("SELECT 1 where @start = @Start", new { Start = 1 }); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Insight.Tests/TestDataClasses.cs: -------------------------------------------------------------------------------- 1 | using Insight.Database; 2 | using NUnit.Framework; 3 | using NUnit.Framework.Legacy; 4 | using System; 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | #pragma warning disable 0649 12 | 13 | namespace Insight.Tests 14 | { 15 | /// 16 | /// Test class to remove some repetition from test cases. 17 | /// 18 | public class ParentTestData 19 | { 20 | public TestData TestData; 21 | public int ParentX; 22 | 23 | public static readonly string Sql = "SELECT ParentX=2, X=5 "; 24 | 25 | public void Verify(bool withGraph = true) 26 | { 27 | ClassicAssert.AreEqual(2, ParentX); 28 | 29 | if (withGraph) 30 | { 31 | ClassicAssert.IsNotNull(TestData); 32 | ClassicAssert.AreEqual(5, TestData.X); 33 | } 34 | else 35 | ClassicAssert.IsNull(TestData); 36 | } 37 | 38 | public static void Verify(IEnumerable results, bool withGraph = true) 39 | { 40 | var list = results.OfType().ToList(); 41 | 42 | ClassicAssert.IsNotNull(results); 43 | ClassicAssert.AreEqual(1, list.Count); 44 | 45 | list[0].Verify(withGraph); 46 | } 47 | } 48 | 49 | /// 50 | /// Test class to remove some repetition from test cases. 51 | /// 52 | public class TestData 53 | { 54 | public int X; 55 | public int Z; 56 | } 57 | 58 | /// 59 | /// Test class to remove some repetition from test cases. 60 | /// 61 | public class TestData2 62 | { 63 | public int Y; 64 | 65 | public static readonly string Sql = "SELECT Y=7 "; 66 | 67 | public static void Verify(IList results) 68 | { 69 | ClassicAssert.IsNotNull(results); 70 | ClassicAssert.AreEqual(1, results.Count); 71 | 72 | var data = results[0]; 73 | ClassicAssert.AreEqual(7, data.Y); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /InsightDatabase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonwagner/Insight.Database/cf55c24cda26a2300f9270a5edcee6ebe473f9f8/InsightDatabase.png -------------------------------------------------------------------------------- /SharedTestConfiguration.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | INSIGHT_FOLDER=$(dirname "$0") 4 | OUTPUT_FOLDER=${INSIGHT_FOLDER}/Build/Output 5 | INSIGHT_CONFIGURATION="${INSIGHT_CONFIGURATION:-Release}" 6 | INSIGHT_NOBUILD= 7 | 8 | build_insight () { 9 | dotnet build -c $INSIGHT_CONFIGURATION ./Insight.sln 10 | } 11 | 12 | while getopts "cdobtp" opt; do 13 | case $opt in 14 | c) 15 | for folder in ./Insight.Database*; do 16 | rm -rf $folder/bin 17 | rm -rf $folder/obj 18 | dotnet clean $folder/*.csproj || break 19 | done 20 | for folder in ./Insight.Tests*; do 21 | rm -rf $folder/bin 22 | rm -rf $folder/obj 23 | dotnet clean $folder/*.csproj || break 24 | done 25 | ;; 26 | d) 27 | echo "Building DEBUG Mode" >&2 28 | INSIGHT_CONFIGURATION=Debug 29 | ;; 30 | o) 31 | echo "Running only-mode --no-build" >&2 32 | INSIGHT_NOBUILD=--no-build 33 | ;; 34 | b) 35 | build_insight 36 | ;; 37 | t) 38 | build_insight 39 | for folder in ./Insight.Tests*; do 40 | dotnet test -c $INSIGHT_CONFIGURATION ${INSIGHT_NOBUILD} $folder/*.csproj || break 41 | done 42 | ;; 43 | p) 44 | echo "Running Package" >&2 45 | rm -rf ${OUTPUT_FOLDER} 46 | mkdir ${OUTPUT_FOLDER} 47 | for folder in ./Insight.Database*; do 48 | dotnet pack -c $INSIGHT_CONFIGURATION ${INSIGHT_NOBUILD} $folder/*.csproj || break 49 | cp $folder/bin/Release/*.nupkg ${OUTPUT_FOLDER} 50 | done 51 | ;; 52 | \?) 53 | echo "Invalid option: -$OPTARG" >&2 54 | exit 1 55 | ;; 56 | esac 57 | done 58 | 59 | if (( $OPTIND == 1 )); then 60 | build_insight 61 | fi -------------------------------------------------------------------------------- /insight-docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | volumes: 4 | db2: 5 | 6 | services: 7 | sqlserver: 8 | image: mcr.microsoft.com/mssql/server:2022-latest 9 | ports: 10 | - "1433:1433" 11 | environment: 12 | - ACCEPT_EULA=y 13 | - MSSQL_SA_PASSWORD=Insight!!!Test 14 | 15 | mysql: 16 | image: mysql/mysql-server:latest 17 | ports: 18 | - "3306:3306" 19 | environment: 20 | - MYSQL_ALLOW_EMPTY_PASSWORD=yes 21 | - MYSQL_ROOT_HOST=% 22 | - MYSQL_DATABASE=test 23 | 24 | postgres: 25 | image: postgres:latest 26 | ports: 27 | - "5432:5432" 28 | environment: 29 | - POSTGRES_PASSWORD=Insight!!!Test 30 | - POSTGRES_USER=postgres 31 | 32 | # docker run --rm -it --name mydb2 --privileged=true -p 50000:50000 -e LICENSE=accept -e DB2INST1_PASSWORD=Insight!!!Test -e DBNAME=testdb -v ./db2:/database ibmcom/db2 33 | # NOTE: this seems to need the local volume instead of mapping to a disk 34 | db2: 35 | image: icr.io/db2_community/db2 36 | platform: linux/amd64 37 | privileged: true 38 | ports: 39 | - "50000:50000" 40 | environment: 41 | - LICENSE=accept 42 | - DB2INSTANCE=db2inst1 43 | - DB2INST1_PASSWORD=Insight!!!Test 44 | - DBNAME=testdb 45 | - PERSISTENT_HOME=false 46 | volumes: 47 | - db2:/database 48 | 49 | # this requires you to log into docker hub and agree to terms of service 50 | oracle: 51 | image: container-registry.oracle.com/database/enterprise:19.19.0.0 52 | ports: 53 | - "1521:1521" 54 | environment: 55 | - WEB_CONSOLE=false 56 | - ORACLE_PWD=Oradoc_db1 57 | -------------------------------------------------------------------------------- /insight-docker/docker-env.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM set the local environment variables 4 | FOR /f "tokens=*" %%i IN ('docker-machine env default') DO @%%i 5 | 6 | REM these variables are read in BaseTest.cs to override localhost and passwords 7 | SET INSIGHT_TEST_HOST=%DOCKER_HOST% 8 | IF "%INSIGHT_TEST_HOST%"=="" SET INSIGHT_TEST_HOST=127.0.0.1 9 | SET INSIGHT_TEST_PASSWORD=Insight!!!Test 10 | 11 | ECHO Insight docker environment variables set 12 | -------------------------------------------------------------------------------- /insight-docker/docker-env.sh: -------------------------------------------------------------------------------- 1 | export INSIGHT_TEST_HOST=$DOCKER_HOST 2 | if [ ! $INSIGHT_TEST_HOST ]; then export INSIGHT_TEST_HOST=127.0.0.1; fi 3 | export INSIGHT_TEST_PASSWORD=Insight!!!Test 4 | 5 | echo $INSIGHT_TEST_HOST 6 | echo $INSIGHT_TEST_PASSWORD 7 | echo Insight docker environment variables set 8 | --------------------------------------------------------------------------------