├── .editorconfig
├── .gitattributes
├── .github
├── FUNDING.yml
├── dependabot.yml
└── workflows
│ ├── dotnet.yml
│ └── merge.yml
├── .gitignore
├── FluentCommand.sln
├── LICENSE
├── README.md
├── coverlet.runsettings
├── docs
├── assets
│ └── logo.png
├── docfx.json
├── guide
│ ├── cache.md
│ ├── configuration.md
│ ├── generation.md
│ ├── logging.md
│ ├── parameter.md
│ ├── query.md
│ ├── sql.md
│ └── toc.yml
├── index.md
├── reference
│ ├── .gitignore
│ └── index.md
├── template
│ └── public
│ │ └── main.css
└── toc.yml
├── logo.png
├── src
├── Directory.Build.props
├── FluentCommand.Batch
│ ├── BatchError.cs
│ ├── BatchFactory.cs
│ ├── BatchJob.cs
│ ├── BatchJobValidator.cs
│ ├── BatchJobVisitor.cs
│ ├── BatchProcessor.cs
│ ├── FieldDefault.cs
│ ├── FieldIndex.cs
│ ├── FieldMapping.cs
│ ├── FieldMatch.cs
│ ├── Fluent
│ │ ├── BatchBuilder.cs
│ │ ├── BatchFieldBuilder.cs
│ │ └── BatchMatchBuilder.cs
│ ├── FluentCommand.Batch.csproj
│ ├── IBatchFactory.cs
│ ├── IBatchProcessor.cs
│ ├── IBatchTranslator.cs
│ ├── IBatchValidator.cs
│ ├── Translators
│ │ └── TranslatorBase.cs
│ └── Validation
│ │ ├── BatchValidator.cs
│ │ └── DuplicateException.cs
├── FluentCommand.Caching
│ ├── DataConfigurationBuilderExtensions.cs
│ ├── DistributedDataCache.cs
│ ├── FluentCommand.Caching.csproj
│ ├── IDistributedCacheSerializer.cs
│ └── MessagePackCacheSerializer.cs
├── FluentCommand.Csv
│ ├── CsvCommandExtensions.cs
│ └── FluentCommand.Csv.csproj
├── FluentCommand.Dapper
│ ├── ConcurrencyTokenTypeHandler.cs
│ ├── DataCommandExtensions.cs
│ ├── FluentCommand.Dapper.csproj
│ └── ReaderFactory.cs
├── FluentCommand.EntityFactory
│ ├── DataCommandExtensions.cs
│ ├── FluentCommand.EntityFactory.csproj
│ ├── ReaderFactory.cs
│ └── Reflection
│ │ └── ReflectionHelper.cs
├── FluentCommand.Generators
│ ├── DataReaderFactoryGenerator.cs
│ ├── DataReaderFactoryWriter.cs
│ ├── DiagnosticDescriptors.cs
│ ├── FluentCommand.Generators.csproj
│ ├── GenerateAttributeGenerator.cs
│ ├── IndentedStringBuilder.cs
│ ├── IsExternalInit.cs
│ ├── Models
│ │ ├── EntityClass.cs
│ │ ├── EntityContext.cs
│ │ ├── EntityProperty.cs
│ │ ├── EquatableArray.cs
│ │ └── InitializationMode.cs
│ ├── Properties
│ │ └── launchSettings.json
│ └── TableAttributeGenerator.cs
├── FluentCommand.Json
│ ├── ConcurrencyTokenJsonConverter.cs
│ ├── FluentCommand.Json.csproj
│ └── JsonCommandExtensions.cs
├── FluentCommand.SqlServer
│ ├── Bulk
│ │ ├── DataBulkCopy.cs
│ │ ├── DataBulkCopyExtensions.cs
│ │ ├── DataBulkCopyMapping.cs
│ │ └── IDataBulkCopy.cs
│ ├── DataConfigurationBuilderExtensions.cs
│ ├── FluentCommand.SqlServer.csproj
│ ├── Import
│ │ ├── FieldDefault.cs
│ │ ├── FieldDefinition.cs
│ │ ├── FieldDefinitionBuilder.cs
│ │ ├── FieldMap.cs
│ │ ├── IFieldTranslator.cs
│ │ ├── IImportProcessor.cs
│ │ ├── IImportValidator.cs
│ │ ├── ImportData.cs
│ │ ├── ImportDefinition.cs
│ │ ├── ImportDefinitionBuilder.cs
│ │ ├── ImportFactory.cs
│ │ ├── ImportFieldMapping.cs
│ │ ├── ImportProcessContext.cs
│ │ ├── ImportProcessor.cs
│ │ ├── ImportResult.cs
│ │ └── ImportValidator.cs
│ ├── Merge
│ │ ├── DataMerge.cs
│ │ ├── DataMergeColumn.cs
│ │ ├── DataMergeColumnMapping.cs
│ │ ├── DataMergeDefinition.cs
│ │ ├── DataMergeExtensions.cs
│ │ ├── DataMergeGenerator.cs
│ │ ├── DataMergeMapping.cs
│ │ ├── DataMergeMode.cs
│ │ ├── DataMergeOutputColumn.cs
│ │ ├── DataMergeOutputRow.cs
│ │ ├── DataReaderWrapper.cs
│ │ ├── IDataColumnMapping.cs
│ │ ├── IDataMerge.cs
│ │ └── IDataMergeMapping.cs
│ ├── Query
│ │ ├── ChangeTableBuilder.cs
│ │ ├── ChangeTableBuilderExtensions.cs
│ │ ├── TemporalBuilder.cs
│ │ └── TemporalBuilderExtensions.cs
│ ├── SqlCommandExtensions.cs
│ └── SqlTypeMapping.cs
└── FluentCommand
│ ├── Attributes
│ └── GenerateReaderAttribute.cs
│ ├── ConcurrencyToken.cs
│ ├── DataCallback.cs
│ ├── DataCommand.cs
│ ├── DataCommandExtensions.cs
│ ├── DataConfiguration.cs
│ ├── DataConfigurationBuilder.cs
│ ├── DataFieldConverterAttribute.cs
│ ├── DataMapping.cs
│ ├── DataParameter.cs
│ ├── DataParameterHandlers.cs
│ ├── DataQueryExtensions.cs
│ ├── DataQueryFormatter.cs
│ ├── DataQueryLogger.cs
│ ├── DataReaderExtensions.cs
│ ├── DataSession.cs
│ ├── DisposableBase.cs
│ ├── Extensions
│ ├── CollectionExtensions.cs
│ ├── ConvertExtensions.cs
│ ├── DataRecordExtensions.cs
│ ├── DataTableExtensions.cs
│ ├── EnumExtensions.cs
│ ├── EnumerableExtensions.cs
│ ├── StringBuilderExtensions.cs
│ ├── StringExtensions.cs
│ └── TypeExtensions.cs
│ ├── FluentCommand.csproj
│ ├── Handlers
│ ├── ConcurrencyTokenHandler.cs
│ ├── DateOnlyHandler.cs
│ └── TimeOnlyHandler.cs
│ ├── IDataCache.cs
│ ├── IDataCommand.cs
│ ├── IDataConfiguration.cs
│ ├── IDataFieldConverter.cs
│ ├── IDataParameter.cs
│ ├── IDataParameterHandler.cs
│ ├── IDataQuery.cs
│ ├── IDataQueryAsync.cs
│ ├── IDataQueryFormatter.cs
│ ├── IDataQueryLogger.cs
│ ├── IDataSession.cs
│ ├── IDataSessionFactory.cs
│ ├── Internal
│ ├── HashCode.cs
│ ├── Internals.cs
│ ├── Singleton.cs
│ └── StringBuilderCache.cs
│ ├── ListDataReader.cs
│ ├── Query
│ ├── AggregateFunctions.cs
│ ├── DeleteBuilder.cs
│ ├── DeleteEntityBuilder.cs
│ ├── FilterOperators.cs
│ ├── Generators
│ │ ├── IQueryGenerator.cs
│ │ ├── PostgresqlGenerator.cs
│ │ ├── QueryExpressions.cs
│ │ ├── SqlServerGenerator.cs
│ │ └── SqliteGenerator.cs
│ ├── IQueryBuilder.cs
│ ├── IQueryStatement.cs
│ ├── IStatementBuilder.cs
│ ├── IWhereEntityBuilder.cs
│ ├── InsertBuilder.cs
│ ├── InsertEntityBuilder.cs
│ ├── JoinBuilder.cs
│ ├── JoinEntityBuilder.cs
│ ├── JoinTypes.cs
│ ├── LogicalBuilder.cs
│ ├── LogicalEntityBuilder.cs
│ ├── LogicalOperators.cs
│ ├── OrderBuilder.cs
│ ├── OrderEntityBuilder.cs
│ ├── QueryBuilder.cs
│ ├── QueryBuilderExtensions.cs
│ ├── QueryParameter.cs
│ ├── QueryStatement.cs
│ ├── SelectBuilder.cs
│ ├── SelectEntityBuilder.cs
│ ├── SortDirections.cs
│ ├── StatementBuilder.cs
│ ├── UpdateBuilder.cs
│ ├── UpdateEntityBuilder.cs
│ ├── WhereBuilder.cs
│ └── WhereEntityBuilder.cs
│ ├── QueryMultipleResult.cs
│ ├── Reflection
│ ├── ExpressionFactory.cs
│ ├── FieldAccessor.cs
│ ├── IMemberAccessor.cs
│ ├── IMemberInformation.cs
│ ├── IMethodAccessor.cs
│ ├── MemberAccessor.cs
│ ├── MethodAccessor.cs
│ ├── PropertyAccessor.cs
│ └── TypeAccessor.cs
│ └── ServiceCollectionExtensions.cs
└── test
├── Directory.Build.props
├── FluentCommand.Batch.Tests
├── DataBulkCopyTests.cs
├── DataMergeGeneratorTests.cs
├── FluentCommand.Batch.Tests.csproj
├── UserProfile.cs
├── appsettings.appveyor.json
└── appsettings.json
├── FluentCommand.Entities
├── Audit.cs
├── Brand.cs
├── DataType.cs
├── FluentCommand.Entities.csproj
├── Internal
│ └── IsExternalInit.cs
├── Member.cs
├── Priority.cs
├── Product.cs
├── ReaderGeneration.cs
├── Role.cs
├── Status.cs
├── StatusConstructor.cs
├── StatusReadOnly.cs
├── StatusRecord.cs
├── TableTest.cs
├── Task.cs
├── TaskExtended.cs
├── User.cs
├── UserImport.cs
├── UserLogin.cs
└── UserRole.cs
├── FluentCommand.Generators.Tests
├── DataReaderFactoryWriterTests.cs
├── FluentCommand.Generators.Tests.csproj
└── Snapshots
│ └── DataReaderFactoryWriterTests.Generate.verified.txt
├── FluentCommand.Performance
├── BenchmarkBase.cs
├── DapperBenchmarks.cs
├── FluentCommand.Performance.csproj
├── FluentCommandBenchmarks.cs
├── HandCodedBenchmarks.cs
├── Post.cs
├── Program.cs
└── PropertyAccessorBenchmark.cs
├── FluentCommand.PostgreSQL.Tests
├── DataCommandSqlAsyncTests.cs
├── DataCommandSqlTests.cs
├── DataQueryTests.cs
├── DatabaseCollection.cs
├── DatabaseFixture.cs
├── DatabaseInitializer.cs
├── DatabaseTestBase.cs
├── FluentCommand.PostgreSQL.Tests.csproj
├── Scripts
│ ├── Script001.Tracker.Schema.sql
│ └── Script002.Tracker.Data.sql
├── appsettings.appveyor.json
├── appsettings.github.json
└── appsettings.json
├── FluentCommand.SQLite.Tests
├── DataCommandSqlAsyncTests.cs
├── DataCommandSqlTests.cs
├── DataQueryTests.cs
├── DatabaseCollection.cs
├── DatabaseFixture.cs
├── DatabaseInitializer.cs
├── DatabaseTestBase.cs
├── FluentCommand.SQLite.Tests.csproj
├── Scripts
│ ├── Script001.Tracker.Schema.sql
│ └── Script002.Tracker.Data.sql
└── appsettings.json
├── FluentCommand.SqlServer.Tests
├── CsvTests.cs
├── DataBulkCopyTests.cs
├── DataCacheTests.cs
├── DataCommandProcedureTests.cs
├── DataCommandSqlAsyncTests.cs
├── DataCommandSqlTests.cs
├── DataConfigurationTests.cs
├── DataMergeGeneratorTests.cs
├── DataMergeTests.cs
├── DataQueryTests.cs
├── DataSessionTests.cs
├── DatabaseCollection.cs
├── DatabaseFixture.cs
├── DatabaseInitializer.cs
├── DatabaseTestBase.cs
├── FluentCommand.SqlServer.Tests.csproj
├── GeneratorTests.cs
├── ImportProcessorTests.cs
├── JsonTests.cs
├── ReadOnlyIntent.cs
├── Scripts
│ ├── Script001.Tracker.Schema.sql
│ ├── Script002.Tracker.Data.sql
│ └── Script003.Tracker.StoredProcedure.sql
├── Snapshots
│ ├── DataMergeGeneratorTests.BuildMergeDataMismatchTests.verified.txt
│ ├── DataMergeGeneratorTests.BuildMergeDataOutputTests.verified.txt
│ ├── DataMergeGeneratorTests.BuildMergeDataTableTests.verified.txt
│ ├── DataMergeGeneratorTests.BuildMergeDataTests.verified.txt
│ ├── DataMergeGeneratorTests.BuildMergeDataTypeTests.verified.txt
│ ├── DataMergeGeneratorTests.BuildMergeTests.verified.txt
│ └── DataMergeGeneratorTests.BuildTableSqlTest.verified.txt
├── appsettings.appveyor.json
├── appsettings.github.json
├── appsettings.json
└── appsettings.linux.json
└── FluentCommand.Tests
├── FluentCommand.Tests.csproj
├── Internal
└── HashCodeTests.cs
├── Models
├── Command.cs
├── Parameter.cs
├── Truck.cs
├── User.cs
└── Vehicle.cs
└── Query
├── DeleteBuilderTest.cs
├── InsertBuilderTest.cs
├── QueryBuilderTests.cs
├── SelectBuilderTest.cs
├── Snapshots
├── DeleteBuilderTest.DeleteEntityJoin.verified.txt
├── DeleteBuilderTest.DeleteEntityWithComment.verified.txt
├── DeleteBuilderTest.DeleteEntityWithOutput.verified.txt
├── InsertBuilderTest.InsertEntityValueWithOutput.verified.txt
├── QueryBuilderTests.QueryBuilderSelect.verified.txt
├── SelectBuilderTest.SelectColumnsAliasWhereTagBuilder.verified.txt
├── SelectBuilderTest.SelectEntityAliasWhereTagBuilder.verified.txt
├── SelectBuilderTest.SelectEntityChangeTableBuilder.verified.txt
├── SelectBuilderTest.SelectEntityFilterBuilder.verified.txt
├── SelectBuilderTest.SelectEntityJoinBuilder.verified.txt
├── SelectBuilderTest.SelectEntityNestedWhereBuilder.verified.txt
├── SelectBuilderTest.SelectEntityTemporalBuilder.verified.txt
├── SelectBuilderTest.SelectEntityWhereIn.verified.txt
├── SelectBuilderTest.SelectEntityWhereInDouble.verified.txt
├── SelectBuilderTest.SelectEntityWhereInIf.verified.txt
├── SelectBuilderTest.SelectEntityWhereLimitBuilder.verified.txt
├── SelectBuilderTest.SelectEntityWhereTagBuilder.verified.txt
├── UpdateBuilderTest.UpdateEntityValueWithJoin.verified.txt
└── UpdateBuilderTest.UpdateEntityValueWithOutput.verified.txt
└── UpdateBuilderTest.cs
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig: https://EditorConfig.org
2 |
3 | root = true
4 |
5 | # All Files
6 | [*]
7 | charset = utf-8
8 | indent_style = space
9 | indent_size = 4
10 | insert_final_newline = true
11 | trim_trailing_whitespace = true
12 |
13 | # XML Configuration Files
14 | [*.{xml,config,props,targets,nuspec,resx,ruleset,vsixmanifest,vsct,refactorlog,runsettings}]
15 | indent_size = 2
16 |
17 | # JSON Files
18 | [*.{json,json5,webmanifest}]
19 | indent_size = 2
20 |
21 | # Project Files
22 | [*.{csproj,sqlproj}]
23 | indent_size = 2
24 |
25 | # YAML Files
26 | [*.{yml,yaml}]
27 | indent_size = 2
28 |
29 | # Markdown Files
30 | [*.md]
31 | trim_trailing_whitespace = false
32 |
33 | # Web Files
34 | [*.{htm,html,js,jsm,ts,tsx,css,sass,scss,less,pcss,svg,vue}]
35 | indent_size = 2
36 |
37 | # Batch Files
38 | [*.{cmd,bat}]
39 | end_of_line = crlf
40 |
41 | # Bash Files
42 | [*.sh]
43 | end_of_line = lf
44 |
45 | [*.{cs,vb}]
46 | dotnet_sort_system_directives_first = true
47 | dotnet_separate_import_directive_groups = true
48 | dotnet_style_namespace_match_folder = true
49 |
50 | [*.cs]
51 | csharp_using_directive_placement = outside_namespace
52 | csharp_style_namespace_declarations = file_scoped:warning
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: loresoft
2 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "github-actions"
4 | directory: "/"
5 | schedule:
6 | interval: daily
7 | time: "01:00"
8 | timezone: "America/Chicago"
9 | open-pull-requests-limit: 10
10 |
11 | - package-ecosystem: nuget
12 | directory: "/"
13 | schedule:
14 | interval: daily
15 | time: "02:00"
16 | timezone: "America/Chicago"
17 | open-pull-requests-limit: 10
18 | ignore:
19 | - dependency-name: "Microsoft.CodeAnalysis.CSharp"
20 | - dependency-name: FluentAssertions
21 | versions: [">=8.0.0"]
22 | groups:
23 | Azure:
24 | patterns:
25 | - "Azure.*"
26 | - "Microsoft.Azure.*"
27 | - "Microsoft.Extensions.Azure"
28 | AspNetCoreHealthChecks:
29 | patterns:
30 | - "AspNetCore.HealthChecks.*"
31 | AspNetCore:
32 | patterns:
33 | - "Microsoft.AspNetCore.*"
34 | - "Microsoft.Extensions.Features"
35 | MicrosoftExtensions:
36 | patterns:
37 | - "Microsoft.Extensions.*"
38 | EntityFrameworkCore:
39 | patterns:
40 | - "Microsoft.EntityFrameworkCore.*"
41 | OpenTelemetry:
42 | patterns:
43 | - "OpenTelemetry.*"
44 | Serilog:
45 | patterns:
46 | - "Serilog"
47 | - "Serilog.*"
48 | Hangfire:
49 | patterns:
50 | - "Hangfire"
51 | - "Hangfire.*"
52 | Testcontainers:
53 | patterns:
54 | - "Testcontainers.*"
55 | xUnit:
56 | patterns:
57 | - "xunit"
58 | - "xunit.assert"
59 | - "xunit.core"
60 | - "xunit.extensibility.*"
61 | - "xunit.runner.*"
62 |
--------------------------------------------------------------------------------
/.github/workflows/merge.yml:
--------------------------------------------------------------------------------
1 | name: Dependabot Auto-Merge
2 | on: pull_request
3 |
4 | permissions:
5 | contents: write
6 | pull-requests: write
7 |
8 | jobs:
9 | dependabot:
10 | runs-on: ubuntu-latest
11 | if: github.actor == 'dependabot[bot]'
12 |
13 | steps:
14 | - name: Dependabot Metadata
15 | id: metadata
16 | uses: dependabot/fetch-metadata@v2
17 | with:
18 | github-token: "${{ secrets.GITHUB_TOKEN }}"
19 |
20 | - name: Dependabot Auto-Merge PRs
21 | run: gh pr merge --auto --merge "$PR_URL"
22 | env:
23 | PR_URL: ${{github.event.pull_request.html_url}}
24 | GH_TOKEN: ${{secrets.GITHUB_TOKEN}}
25 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 LoreSoft
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/coverlet.runsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | lcov
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/docs/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loresoft/FluentCommand/e8ba79e74888389f87831889a6d983f8736ec7c2/docs/assets/logo.png
--------------------------------------------------------------------------------
/docs/docfx.json:
--------------------------------------------------------------------------------
1 | {
2 | "metadata": [
3 | {
4 | "src": [
5 | {
6 | "files": [
7 | "src/FluentCommand/bin/Release/net8.0/FluentCommand.dll",
8 | "src/FluentCommand.SqlServer/bin/Release/net8.0/FluentCommand.SqlServer.dll",
9 | "src/FluentCommand.Json/bin/Release/net8.0/FluentCommand.Json.dll"
10 | ],
11 | "src": "../"
12 | }
13 | ],
14 | "dest": "reference"
15 | }
16 | ],
17 | "build": {
18 | "content": [
19 | {
20 | "files": [
21 | "reference/**.yml",
22 | "reference/index.md"
23 | ]
24 | },
25 | {
26 | "files": [
27 | "guide/**.md",
28 | "guide/**/toc.yml",
29 | "toc.yml",
30 | "*.md"
31 | ]
32 | }
33 | ],
34 | "resource": [
35 | {
36 | "files": [
37 | "assets/**"
38 | ]
39 | }
40 | ],
41 | "postProcessors": [
42 | "ExtractSearchIndex"
43 | ],
44 | "globalMetadata": {
45 | "_appTitle": "FluentCommand",
46 | "_appName": "FluentCommand",
47 | "_appFooter": "Copyright © 2024 LoreSoft",
48 | "_appLogoPath": "assets/logo.png",
49 | "_appFaviconPath": "assets/logo.png",
50 | "_enableSearch": true
51 | },
52 | "sitemap": {
53 | "baseUrl": "https://loresoft.com/FluentCommand",
54 | "priority": 0.5,
55 | "changefreq": "daily"
56 | },
57 | "output": "_site",
58 | "template": [
59 | "default",
60 | "modern",
61 | "template"
62 | ]
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/docs/guide/cache.md:
--------------------------------------------------------------------------------
1 | # Caching
2 |
--------------------------------------------------------------------------------
/docs/guide/configuration.md:
--------------------------------------------------------------------------------
1 | # Configuration
2 |
3 | ## Configuration for SQL Server
4 |
5 | ```csharp
6 | var dataConfiguration = new DataConfiguration(
7 | SqlClientFactory.Instance,
8 | ConnectionString
9 | );
10 | ```
11 |
12 | ## Configure data logger
13 |
14 | ```csharp
15 | var dataLogger = new DataQueryLogger(Output.WriteLine);
16 | var dataConfiguration = new DataConfiguration(
17 | SqlClientFactory.Instance,
18 | ConnectionString,
19 | queryLogger: dataLogger
20 | );
21 | ```
22 |
23 | ## Register with dependency injection
24 |
25 | ```csharp
26 | services.AddFluentCommand(builder => builder
27 | .UseConnectionString(ConnectionString)
28 | .UseSqlServer()
29 | );
30 | ```
31 |
32 | ## Register using a connection name from the appsettings.json
33 |
34 | ```csharp
35 | services.AddFluentCommand(builder => builder
36 | .UseConnectionName("Tracker")
37 | .UseSqlServer()
38 | );
39 | ```
40 |
41 | ```json
42 | {
43 | "ConnectionStrings": {
44 | "Tracker": "Data Source=(local);Initial Catalog=TrackerTest;Integrated Security=True;TrustServerCertificate=True;"
45 | }
46 | }
47 | ```
48 |
49 | ## Register for PostgreSQL
50 |
51 | ```csharp
52 | services.AddFluentCommand(builder => builder
53 | .UseConnectionName("Tracker")
54 | .AddProviderFactory(NpgsqlFactory.Instance)
55 | .AddPostgreSqlGenerator()
56 | );
57 | ```
58 |
--------------------------------------------------------------------------------
/docs/guide/generation.md:
--------------------------------------------------------------------------------
1 | # Source Generator
2 |
3 | The project supports generating a DbDataReader from a class via an attribute. Add the `TableAttribute` to a class to generate the needed extension methods.
4 |
5 | ```c#
6 | [Table("Status", Schema = "dbo")]
7 | public class Status
8 | {
9 | public int Id { get; set; }
10 | public string Name { get; set; }
11 | public string Description { get; set; }
12 | public int DisplayOrder { get; set; }
13 | public bool IsActive { get; set; }
14 | public DateTimeOffset Created { get; set; }
15 | public string CreatedBy { get; set; }
16 | public DateTimeOffset Updated { get; set; }
17 | public string UpdatedBy { get; set; }
18 |
19 | [ConcurrencyCheck]
20 | [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
21 | [DataFieldConverter(typeof(ConcurrencyTokenHandler))]
22 | public ConcurrencyToken RowVersion { get; set; }
23 |
24 | [NotMapped]
25 | public virtual ICollection Tasks { get; set; } = new List();
26 | }
27 | ```
28 |
29 | Extension methods are generated to materialize data command to entities
30 |
31 | ```c#
32 | string email = "kara.thrace@battlestar.com";
33 | string sql = "select * from [User] where EmailAddress = @EmailAddress";
34 | var session = configuration.CreateSession();
35 | var user = await session
36 | .Sql(sql)
37 | .Parameter("@EmailAddress", email)
38 | .QuerySingleAsync();
39 | ```
40 |
--------------------------------------------------------------------------------
/docs/guide/logging.md:
--------------------------------------------------------------------------------
1 | # Logging
2 |
--------------------------------------------------------------------------------
/docs/guide/toc.yml:
--------------------------------------------------------------------------------
1 | - name: Configuration
2 | href: configuration.md
3 | - name: SQL Builder
4 | href: sql.md
5 | - name: Parameters
6 | href: parameter.md
7 | - name: Queries
8 | href: query.md
9 | - name: Caching
10 | href: cache.md
11 | - name: Logging
12 | href: logging.md
13 | - name: Generation
14 | href: generation.md
15 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | # FluentCommand
2 |
3 | Fluent Wrapper for DbCommand.
4 |
5 | [](https://github.com/loresoft/FluentCommand/actions)
6 |
7 | [](https://coveralls.io/github/loresoft/FluentCommand?branch=master)
8 |
9 | | Package | Version |
10 | | :--- | :--- |
11 | | [FluentCommand](https://www.nuget.org/packages/FluentCommand/) | [](https://www.nuget.org/packages/FluentCommand/) |
12 | | [FluentCommand.SqlServer](https://www.nuget.org/packages/FluentCommand.SqlServer/) | [](https://www.nuget.org/packages/FluentCommand.SqlServer/) |
13 | | [FluentCommand.Json](https://www.nuget.org/packages/FluentCommand.Json/) | [](https://www.nuget.org/packages/FluentCommand.Json/) |
14 |
15 | ## Download
16 |
17 | The FluentCommand library is available on nuget.org via package name `FluentCommand`.
18 |
19 | To install FluentCommand, run the following command in the Package Manager Console
20 |
21 | PM> Install-Package FluentCommand
22 |
23 | More information about NuGet package available at
24 |
25 |
26 | ## Features
27 |
28 | - Fluent wrapper over DbConnection and DbCommand
29 | - Callback for parameter return values
30 | - Automatic handling of connection state
31 | - Caching of results
32 | - Automatic creating of entity from DataReader via Dapper
33 | - Create Dynamic objects from DataReader via Dapper
34 | - Handles multiple result sets
35 | - Basic SQL query builder
36 | - Source Generate DataReader
37 |
--------------------------------------------------------------------------------
/docs/reference/.gitignore:
--------------------------------------------------------------------------------
1 | *.yml
2 | .manifest
3 |
--------------------------------------------------------------------------------
/docs/reference/index.md:
--------------------------------------------------------------------------------
1 | # FluentCommand Reference
2 |
3 | The API Reference Documentation
4 |
--------------------------------------------------------------------------------
/docs/template/public/main.css:
--------------------------------------------------------------------------------
1 | #logo {
2 | width: 50px;
3 | height: 50px;
4 | margin-right: 0.5em;
5 | }
6 |
7 | #breadcrumb {
8 | display: none;
9 | }
10 |
--------------------------------------------------------------------------------
/docs/toc.yml:
--------------------------------------------------------------------------------
1 | - name: User Guide
2 | href: guide/
3 | - name: Reference
4 | href: reference/
5 | homepage: reference/index.md
6 |
--------------------------------------------------------------------------------
/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/loresoft/FluentCommand/e8ba79e74888389f87831889a6d983f8736ec7c2/logo.png
--------------------------------------------------------------------------------
/src/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Fluent Wrapper for DbCommand
5 | Copyright © $([System.DateTime]::Now.ToString(yyyy)) LoreSoft
6 | LoreSoft
7 | en-US
8 | true
9 | orm;sql;micro-orm;database
10 | https://github.com/loresoft/FluentCommand
11 | MIT
12 | logo.png
13 | README.md
14 | git
15 | https://github.com/loresoft/FluentCommand
16 | true
17 |
18 |
19 |
20 | embedded
21 | true
22 | false
23 |
24 |
25 |
26 | true
27 |
28 |
29 |
30 | en-US
31 | latest
32 | enable
33 | 1591
34 |
35 |
36 |
37 | v
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | true
48 | \
49 | false
50 |
51 |
52 | true
53 | \
54 | false
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/src/FluentCommand.Batch/BatchError.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel;
2 |
3 | namespace FluentCommand.Batch;
4 |
5 | ///
6 | /// How to handle batch errors
7 | ///
8 | public enum BatchError
9 | {
10 | ///
11 | /// Skip the error and move to next row
12 | ///
13 | [Description("Skip error and continue")]
14 | Skip,
15 | ///
16 | /// Quit processing batch
17 | ///
18 | [Description("Stop processing")]
19 | Quit
20 | }
--------------------------------------------------------------------------------
/src/FluentCommand.Batch/BatchFactory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Concurrent;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 |
6 | using FluentCommand.Batch.Validation;
7 |
8 | namespace FluentCommand.Batch;
9 |
10 | public class BatchFactory : IBatchFactory
11 | {
12 | private readonly List _validators;
13 | private readonly List _translators;
14 |
15 |
16 | public BatchFactory(IEnumerable validators, IEnumerable translators)
17 | {
18 | _validators = validators?.ToList() ?? new List();
19 | _translators = translators?.ToList() ?? new List();
20 | }
21 |
22 | ///
23 | /// Resolves the field translator for the specified name.
24 | ///
25 | /// The name of the translator.
26 | ///
27 | /// An instance of if found; otherwise null.
28 | ///
29 | public IBatchTranslator ResolveTranslator(string name)
30 | {
31 | if (string.IsNullOrWhiteSpace(name))
32 | return null;
33 |
34 | return _translators
35 | .FirstOrDefault(t => t.GetType().Name == name);
36 | }
37 |
38 | ///
39 | /// Resolves the row validator for the specified name.
40 | ///
41 | /// The name of the validator.
42 | ///
43 | /// An instance of if found; otherwise null.
44 | ///
45 | public IBatchValidator ResolveValidator(string name)
46 | {
47 | if (string.IsNullOrWhiteSpace(name))
48 | return null;
49 |
50 | return _validators
51 | .FirstOrDefault(t => t.GetType().Name == name);
52 |
53 | }
54 | }
--------------------------------------------------------------------------------
/src/FluentCommand.Batch/BatchJobValidator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel.DataAnnotations;
3 | using System.Linq;
4 |
5 | namespace FluentCommand.Batch;
6 |
7 | ///
8 | /// A class to validate an instance of
9 | ///
10 | public class BatchJobValidator : BatchJobVisitor
11 | {
12 | ///
13 | /// Validates the specified .
14 | ///
15 | /// The to validate.
16 | ///
17 | public virtual bool Validate(BatchJob batchJob)
18 | {
19 | Visit(batchJob);
20 |
21 | return true;
22 | }
23 |
24 | ///
25 | /// Visits the specified .
26 | ///
27 | /// The to visit.
28 | ///
29 | /// Missing key column. Please select a column to be the key.
30 | /// or
31 | /// Missing column selection. Please select a column to be included.
32 | ///
33 | public override void Visit(BatchJob batchJob)
34 | {
35 | // must have key
36 | var keyColumn = batchJob.Fields.FirstOrDefault(m => m.IsKey);
37 | if (keyColumn == null)
38 | throw new ValidationException("Missing key field. Please select a field to be the key.");
39 |
40 | // must have column
41 | bool hasColumn = batchJob.Fields.Any(m => (m.Index.HasValue || m.Default.HasValue) && !m.IsKey);
42 | if (!hasColumn)
43 | throw new ValidationException("Missing field selection. Please select a field to be included.");
44 |
45 | base.Visit(batchJob);
46 | }
47 |
48 | ///
49 | /// Visits the specified .
50 | ///
51 | /// The to visit.
52 | /// Missing source column mapping. Please select a source column.
53 | public override void VisitFieldMapping(FieldMapping fieldMapping)
54 | {
55 | var c = fieldMapping;
56 |
57 | if (c.Required && (c.Index == null || c.Index == -1))
58 | throw new ValidationException("Missing required field mapping. Please select a source field for '" + c.DisplayName + "'.");
59 |
60 | base.VisitFieldMapping(fieldMapping);
61 | }
62 | }
--------------------------------------------------------------------------------
/src/FluentCommand.Batch/BatchJobVisitor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace FluentCommand.Batch;
6 |
7 | ///
8 | /// A class that represents a visitor for .
9 | ///
10 | public abstract class BatchJobVisitor
11 | {
12 | ///
13 | /// Visits the specified .
14 | ///
15 | /// The to visit.
16 | public virtual void Visit(BatchJob batchJob)
17 | {
18 | foreach (var item in batchJob.Fields)
19 | VisitFieldMapping(item);
20 |
21 | }
22 |
23 | ///
24 | /// Visits the specified .
25 | ///
26 | /// The to visit.
27 | public virtual void VisitFieldMapping(FieldMapping fieldMapping)
28 | {
29 | foreach (var item in fieldMapping.MatchDefinitions)
30 | VisitFieldMatch(item);
31 |
32 | }
33 |
34 | ///
35 | /// Visits the specified .
36 | ///
37 | /// The to visit.
38 | public virtual void VisitFieldMatch(FieldMatch fieldMatch)
39 | {
40 | }
41 | }
42 |
43 |
--------------------------------------------------------------------------------
/src/FluentCommand.Batch/FieldDefault.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace FluentCommand.Batch;
4 |
5 | ///
6 | /// Default field value
7 | ///
8 | public enum FieldDefault
9 | {
10 | ///
11 | /// Use the as the default value.
12 | ///
13 | UserName,
14 | ///
15 | /// Use the current date time as the default value.
16 | ///
17 | CurrentDate,
18 | ///
19 | /// Use a static default value.
20 | ///
21 | Static
22 | }
--------------------------------------------------------------------------------
/src/FluentCommand.Batch/FieldIndex.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace FluentCommand.Batch;
4 |
5 | ///
6 | /// Field definition
7 | ///
8 | public class FieldIndex
9 | {
10 | ///
11 | /// Gets or sets the field index.
12 | ///
13 | ///
14 | /// The field index.
15 | ///
16 | public int? Index { get; set; }
17 |
18 | ///
19 | /// Gets or sets the name of the field.
20 | ///
21 | ///
22 | /// The name of the field.
23 | ///
24 | public string Name { get; set; }
25 |
26 | ///
27 | /// Returns a that represents this instance.
28 | ///
29 | ///
30 | /// A that represents this instance.
31 | ///
32 | public override string ToString()
33 | {
34 | return $"Name: {Name}, Index: {Index}";
35 | }
36 | }
--------------------------------------------------------------------------------
/src/FluentCommand.Batch/FieldMatch.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 FluentCommand.Batch;
8 |
9 | ///
10 | /// A class to define a field match for field mapping.
11 | ///
12 | public class FieldMatch
13 | {
14 | ///
15 | /// Gets or sets the text used to match a field name. If is true, this will be used as a regular expression.
16 | ///
17 | ///
18 | /// The text used to match a field name.
19 | ///
20 | public string Text { get; set; }
21 |
22 | ///
23 | /// Gets or sets the default translator source when matched.
24 | ///
25 | ///
26 | /// The translator source.
27 | ///
28 | public string TranslatorSource { get; set; }
29 |
30 | ///
31 | /// Gets or sets a value indicating whether to use as regular expression.
32 | ///
33 | ///
34 | /// true to use regex; otherwise, false.
35 | ///
36 | public bool UseRegex { get; set; }
37 | }
38 |
--------------------------------------------------------------------------------
/src/FluentCommand.Batch/Fluent/BatchMatchBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace FluentCommand.Batch.Fluent;
4 |
5 | public class BatchMatchBuilder
6 | {
7 | private readonly FieldMatch _fieldMatch;
8 |
9 | public BatchMatchBuilder(FieldMatch fieldMatch)
10 | {
11 | _fieldMatch = fieldMatch;
12 | }
13 |
14 | public BatchMatchBuilder Text(string value)
15 | {
16 | _fieldMatch.Text = value;
17 | return this;
18 | }
19 |
20 | public BatchMatchBuilder UseRegex(bool value = true)
21 | {
22 | _fieldMatch.UseRegex = value;
23 | return this;
24 | }
25 |
26 | public BatchMatchBuilder TranslatorSource(string value)
27 | {
28 | _fieldMatch.TranslatorSource = value;
29 | return this;
30 | }
31 | }
--------------------------------------------------------------------------------
/src/FluentCommand.Batch/FluentCommand.Batch.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netstandard2.0;net6.0;net7.0
4 |
5 |
6 |
7 | 1701;1702;1591
8 | latest
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/FluentCommand.Batch/IBatchFactory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace FluentCommand.Batch;
4 |
5 | ///
6 | /// An for resolving named type
7 | ///
8 | public interface IBatchFactory
9 | {
10 | ///
11 | /// Resolves the field translator for the specified name.
12 | ///
13 | /// The name of the translator.
14 | /// An instance of if found; otherwise null.
15 | IBatchTranslator ResolveTranslator(string name);
16 |
17 | ///
18 | /// Resolves the row validator for the specified name.
19 | ///
20 | /// The name of the validator.
21 | /// An instance of if found; otherwise null.
22 | IBatchValidator ResolveValidator(string name);
23 | }
--------------------------------------------------------------------------------
/src/FluentCommand.Batch/IBatchProcessor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Data;
4 |
5 | using FluentCommand.Merge;
6 |
7 | namespace FluentCommand.Batch;
8 |
9 | public interface IBatchProcessor
10 | {
11 | IEnumerable Process(BatchJob batchJob);
12 | DataTable CreateTable(BatchJob batchJob);
13 | }
--------------------------------------------------------------------------------
/src/FluentCommand.Batch/IBatchTranslator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace FluentCommand.Batch;
4 |
5 | public interface IBatchTranslator
6 | {
7 | string[] Sources { get; }
8 |
9 | object Translate(string source, object original);
10 | }
--------------------------------------------------------------------------------
/src/FluentCommand.Batch/IBatchValidator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Data;
3 |
4 | namespace FluentCommand.Batch;
5 |
6 | public interface IBatchValidator
7 | {
8 | void Reset();
9 |
10 | void ValidateRow(BatchJob batchJob, DataRow targetRow);
11 | }
--------------------------------------------------------------------------------
/src/FluentCommand.Batch/Validation/DuplicateException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace FluentCommand.Batch.Validation;
4 |
5 | public class DuplicateException : Exception
6 | {
7 | public DuplicateException(string message)
8 | : base(message)
9 | {
10 | }
11 |
12 | public DuplicateException(string message, Exception innerException)
13 | : base(message, innerException)
14 | {
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/FluentCommand.Caching/DataConfigurationBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | using MessagePack;
2 | using MessagePack.Resolvers;
3 |
4 | using Microsoft.Extensions.DependencyInjection.Extensions;
5 |
6 | namespace FluentCommand.Caching;
7 |
8 | ///
9 | /// Extension methods for
10 | ///
11 | public static class DataConfigurationBuilderExtensions
12 | {
13 | ///
14 | /// Adds the distributed data cache.
15 | ///
16 | /// The data configuration builder.
17 | ///
18 | public static DataConfigurationBuilder AddDistributedDataCache(this DataConfigurationBuilder builder)
19 | {
20 | builder.AddService(sp =>
21 | {
22 | sp.TryAddSingleton(ContractlessStandardResolver.Options.WithCompression(MessagePackCompression.Lz4BlockArray));
23 | sp.TryAddSingleton();
24 | });
25 |
26 | builder.AddDataCache();
27 |
28 | return builder;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/FluentCommand.Caching/FluentCommand.Caching.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0;net8.0;net9.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/FluentCommand.Caching/IDistributedCacheSerializer.cs:
--------------------------------------------------------------------------------
1 | namespace FluentCommand.Caching;
2 |
3 | ///
4 | /// Interface defining a serializer of distrubted cache
5 | ///
6 | public interface IDistributedCacheSerializer
7 | {
8 | ///
9 | /// Serializes the specified instance to a byte array for caching.
10 | ///
11 | /// The type to serialize
12 | /// The instance to serialize.
13 | ///
14 | /// The byte array of the serialized instance
15 | ///
16 | byte[] Serialize(T instance);
17 |
18 | ///
19 | /// Serializes the specified instance to a byte array for caching.
20 | ///
21 | /// The type to serialize
22 | /// The instance to serialize.
23 | /// The cancellation token.
24 | ///
25 | /// The byte array of the serialized instance
26 | ///
27 | Task SerializeAsync(T instance, CancellationToken cancellationToken = default);
28 |
29 | ///
30 | /// Deserializes the specified byte array into an instance of .
31 | ///
32 | /// The type to deserialize
33 | /// The byte array to deserialize.
34 | ///
35 | /// An instance of deserialized
36 | ///
37 | T Deserialize(byte[] byteArray);
38 |
39 | ///
40 | /// Deserializes the specified byte array into an instance of .
41 | ///
42 | /// The type to deserialize
43 | /// The byte array to deserialize.
44 | /// The cancellation token.
45 | ///
46 | /// An instance of deserialized
47 | ///
48 | Task DeserializeAsync(byte[] byteArray, CancellationToken cancellationToken = default);
49 | }
50 |
--------------------------------------------------------------------------------
/src/FluentCommand.Csv/FluentCommand.Csv.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0;net8.0;net9.0
5 | FluentCommand
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/FluentCommand.Dapper/ConcurrencyTokenTypeHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Data;
2 |
3 | using Dapper;
4 |
5 | namespace FluentCommand;
6 |
7 | public class ConcurrencyTokenTypeHandler : SqlMapper.TypeHandler
8 | {
9 | public override ConcurrencyToken Parse(object value)
10 | {
11 | return value switch
12 | {
13 | string textToken => new ConcurrencyToken(textToken),
14 | byte[] byteToken => new ConcurrencyToken(byteToken),
15 | _ => ConcurrencyToken.None
16 | };
17 | }
18 |
19 | public override void SetValue(IDbDataParameter parameter, ConcurrencyToken value)
20 | {
21 | parameter.Value = value.Value;
22 | parameter.DbType = DbType.Binary;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/FluentCommand.Dapper/FluentCommand.Dapper.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0;net6.0;net7.0
5 | FluentCommand
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/FluentCommand.Dapper/ReaderFactory.cs:
--------------------------------------------------------------------------------
1 | using System.Data;
2 |
3 | using Dapper;
4 |
5 | using static Dapper.SqlMapper;
6 |
7 | namespace FluentCommand;
8 |
9 | ///
10 | /// A class with data reader factory methods.
11 | ///
12 | public static class ReaderFactory
13 | {
14 | ///
15 | /// A factory for creating TEntity objects from the current row in the .
16 | ///
17 | /// The type of the entity.
18 | /// The open to get the object from.
19 | /// A TEntity object having property names set that match the field names in the .
20 | public static TEntity EntityFactory(IDataReader reader)
21 | where TEntity : class
22 | {
23 | // parser is cached in dapper, ok to repeated calls
24 | var parser = reader.GetRowParser();
25 | return parser(reader);
26 | }
27 |
28 | ///
29 | /// A factory for creating dynamic objects from the current row in the .
30 | ///
31 | /// The open to get the object from.
32 | /// A dynamic object having property names set that match the field names in the .
33 | public static dynamic DynamicFactory(IDataReader reader)
34 | {
35 | // parser is cached in dapper, ok to repeated calls
36 | var parser = reader.GetRowParser();
37 | return parser(reader);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/FluentCommand.EntityFactory/FluentCommand.EntityFactory.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netstandard2.0;net6.0;net7.0
4 | FluentCommand
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/FluentCommand.EntityFactory/ReaderFactory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Data;
4 | using System.Dynamic;
5 |
6 | using FluentCommand.Reflection;
7 |
8 | namespace FluentCommand;
9 |
10 | ///
11 | /// A class with data reader factory methods.
12 | ///
13 | public static class ReaderFactory
14 | {
15 | ///
16 | /// A factory for creating TEntity objects from the current row in the .
17 | ///
18 | /// The type of the entity.
19 | /// The open to get the object from.
20 | /// A TEntity object having property names set that match the field names in the .
21 | public static TEntity EntityFactory(IDataReader reader)
22 | where TEntity : class, new()
23 | {
24 | var entityAccessor = TypeAccessor.GetAccessor();
25 | var entity = new TEntity();
26 |
27 | for (int i = 0; i < reader.FieldCount; i++)
28 | {
29 | if (reader.IsDBNull(i))
30 | continue;
31 |
32 | var name = reader.GetName(i);
33 |
34 | var memberAccessor = entityAccessor.FindColumn(name);
35 | if (memberAccessor == null)
36 | continue;
37 |
38 | var value = reader.GetValue(i);
39 | var fieldType = reader.GetFieldType(i);
40 |
41 | var coerceValue = ReflectionHelper.CoerceValue(memberAccessor.MemberType, fieldType, value);
42 | memberAccessor.SetValue(entity, coerceValue);
43 | }
44 |
45 | return entity;
46 | }
47 |
48 | ///
49 | /// A factory for creating dynamic objects from the current row in the .
50 | ///
51 | /// The open to get the object from.
52 | /// A dynamic object having property names set that match the field names in the .
53 | public static dynamic DynamicFactory(IDataReader reader)
54 | {
55 | dynamic expando = new ExpandoObject();
56 | var dictionary = expando as IDictionary;
57 |
58 | for (int i = 0; i < reader.FieldCount; i++)
59 | dictionary.Add(reader.GetName(i), reader[i]);
60 |
61 | return expando;
62 | }
63 | }
--------------------------------------------------------------------------------
/src/FluentCommand.Generators/DiagnosticDescriptors.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 |
3 | namespace FluentCommand.Generators;
4 |
5 | public static class DiagnosticDescriptors
6 | {
7 | public static DiagnosticDescriptor InvalidConstructor { get; } = new DiagnosticDescriptor(
8 | id: "FCG1001",
9 | title: "Invalid Constructor",
10 | messageFormat: "Count not find a constructor with {0} parameter(s) for type {1}. Classes initialized via constructor need to have the same number of parameters as public properties.",
11 | category: "FluentCommandGenerator",
12 | DiagnosticSeverity.Error,
13 | isEnabledByDefault: true);
14 |
15 | public static DiagnosticDescriptor InvalidConstructorParameter { get; } = new DiagnosticDescriptor(
16 | id: "FCG1002",
17 | title: "Invalid Constructor Parameter",
18 | messageFormat: "Count not find a constructor parameter {0} for type {1}. Classes initialized via constructor need to have parameters that match the properties in the class",
19 | category: "FluentCommandGenerator",
20 | DiagnosticSeverity.Warning,
21 | isEnabledByDefault: true);
22 | }
23 |
--------------------------------------------------------------------------------
/src/FluentCommand.Generators/FluentCommand.Generators.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 | true
7 |
8 | true
9 | true
10 | false
11 | true
12 | false
13 |
14 | cs
15 | 4.4
16 |
17 | true
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/FluentCommand.Generators/GenerateAttributeGenerator.cs:
--------------------------------------------------------------------------------
1 | using FluentCommand.Generators.Models;
2 |
3 | using Microsoft.CodeAnalysis;
4 |
5 | namespace FluentCommand.Generators;
6 |
7 | [Generator(LanguageNames.CSharp)]
8 | public class GenerateAttributeGenerator : DataReaderFactoryGenerator, IIncrementalGenerator
9 | {
10 | public void Initialize(IncrementalGeneratorInitializationContext context)
11 | {
12 | var provider = context.SyntaxProvider.ForAttributeWithMetadataName(
13 | fullyQualifiedMetadataName: "FluentCommand.Attributes.GenerateReaderAttribute",
14 | predicate: SyntacticPredicate,
15 | transform: SemanticTransform
16 | )
17 | .Where(static context => context is not null);
18 |
19 | // Emit the diagnostics, if needed
20 | var diagnostics = provider
21 | .Select(static (item, _) => item.Diagnostics)
22 | .Where(static item => item.Count > 0);
23 |
24 | context.RegisterSourceOutput(diagnostics, ReportDiagnostic);
25 |
26 | var entityClasses = provider
27 | .SelectMany(static (item, _) => item.EntityClasses)
28 | .Where(static item => item is not null);
29 |
30 | context.RegisterSourceOutput(entityClasses, WriteSource);
31 | }
32 |
33 | private static bool SyntacticPredicate(SyntaxNode syntaxNode, CancellationToken cancellationToken)
34 | {
35 | return true;
36 | }
37 |
38 | private static EntityContext SemanticTransform(GeneratorAttributeSyntaxContext context, CancellationToken cancellationToken)
39 | {
40 | if (context.Attributes.Length == 0)
41 | return null;
42 |
43 | var classes = new List();
44 | var diagnostics = new List();
45 |
46 | foreach (var attribute in context.Attributes)
47 | {
48 | if (attribute == null)
49 | return null;
50 |
51 | if (attribute.ConstructorArguments.Length != 1)
52 | return null;
53 |
54 | var comparerArgument = attribute.ConstructorArguments[0];
55 | if (comparerArgument.Value is not INamedTypeSymbol targetSymbol)
56 | return null;
57 |
58 | var entityClass = CreateClass(context.TargetNode.GetLocation(), targetSymbol, diagnostics);
59 | if (entityClass != null)
60 | classes.Add(entityClass);
61 | }
62 |
63 | return new EntityContext(classes, diagnostics);
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/FluentCommand.Generators/IsExternalInit.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel;
2 |
3 | namespace System.Runtime.CompilerServices;
4 |
5 | [EditorBrowsable(EditorBrowsableState.Never)]
6 | internal static class IsExternalInit;
7 |
--------------------------------------------------------------------------------
/src/FluentCommand.Generators/Models/EntityClass.cs:
--------------------------------------------------------------------------------
1 | namespace FluentCommand.Generators.Models;
2 |
3 | public record EntityClass(
4 | InitializationMode InitializationMode,
5 | string FullyQualified,
6 | string EntityNamespace,
7 | string EntityName,
8 | EquatableArray Properties
9 | );
10 |
--------------------------------------------------------------------------------
/src/FluentCommand.Generators/Models/EntityContext.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 |
3 | namespace FluentCommand.Generators.Models;
4 |
5 | public record EntityContext(
6 | EquatableArray EntityClasses,
7 | EquatableArray Diagnostics
8 | );
9 |
--------------------------------------------------------------------------------
/src/FluentCommand.Generators/Models/EntityProperty.cs:
--------------------------------------------------------------------------------
1 | namespace FluentCommand.Generators.Models;
2 |
3 | public record EntityProperty(
4 | string PropertyName,
5 | string ColumnName,
6 | string PropertyType,
7 | string ParameterName = null,
8 | string ConverterName = null
9 | );
10 |
--------------------------------------------------------------------------------
/src/FluentCommand.Generators/Models/EquatableArray.cs:
--------------------------------------------------------------------------------
1 | using System.Collections;
2 | using System.Diagnostics.CodeAnalysis;
3 |
4 | #nullable enable
5 |
6 | namespace FluentCommand.Generators.Models;
7 |
8 | [ExcludeFromCodeCoverage]
9 | public readonly struct EquatableArray : IEquatable>, IEnumerable
10 | where T : IEquatable
11 | {
12 | public static readonly EquatableArray Empty = new();
13 |
14 |
15 | public EquatableArray() : this([]) { }
16 |
17 | public EquatableArray(T[] array) => Array = array ?? [];
18 |
19 | public EquatableArray(IEnumerable items) => Array = items.ToArray() ?? [];
20 |
21 |
22 | public T[] Array { get; }
23 |
24 | public int Count => Array.Length;
25 |
26 |
27 | public ReadOnlySpan AsSpan() => Array.AsSpan();
28 |
29 | public T[] AsArray() => Array;
30 |
31 |
32 | public static bool operator ==(EquatableArray left, EquatableArray right) => left.Equals(right);
33 |
34 | public static bool operator !=(EquatableArray left, EquatableArray right) => !left.Equals(right);
35 |
36 | public bool Equals(EquatableArray array) => Array.AsSpan().SequenceEqual(array.AsSpan());
37 |
38 | public override bool Equals(object? obj) => obj is EquatableArray array && Equals(this, array);
39 |
40 | public override int GetHashCode()
41 | {
42 | if (Array is not T[] array)
43 | return 0;
44 |
45 | var hashCode = 16777619;
46 |
47 | for (int i = 0; i < array.Length; i++)
48 | hashCode = unchecked((hashCode * -1521134295) + EqualityComparer.Default.GetHashCode(array[i]));
49 |
50 | return hashCode;
51 | }
52 |
53 |
54 | IEnumerator IEnumerable.GetEnumerator() => (Array as IEnumerable).GetEnumerator();
55 |
56 | IEnumerator IEnumerable.GetEnumerator() => Array.GetEnumerator();
57 |
58 |
59 | public static implicit operator EquatableArray(T[] array) => new(array);
60 |
61 | public static implicit operator EquatableArray(List items) => new(items);
62 | }
63 |
--------------------------------------------------------------------------------
/src/FluentCommand.Generators/Models/InitializationMode.cs:
--------------------------------------------------------------------------------
1 | namespace FluentCommand.Generators.Models;
2 |
3 | public enum InitializationMode
4 | {
5 | ObjectInitializer,
6 | Constructor
7 | }
8 |
--------------------------------------------------------------------------------
/src/FluentCommand.Generators/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "FluentCommand.Tests": {
4 | "commandName": "DebugRoslynComponent",
5 | "targetProject": "..\\..\\test\\FluentCommand.Tests\\FluentCommand.Tests.csproj"
6 | },
7 | "FluentCommand.Entities": {
8 | "commandName": "DebugRoslynComponent",
9 | "targetProject": "..\\..\\test\\FluentCommand.Entities\\FluentCommand.Entities.csproj"
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/FluentCommand.Json/ConcurrencyTokenJsonConverter.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace FluentCommand;
5 |
6 | ///
7 | /// Json Converter for
8 | ///
9 | public class ConcurrencyTokenJsonConverter : JsonConverter
10 | {
11 | ///
12 | /// Read and convert the JSON to T.
13 | ///
14 | /// The to read from.
15 | /// The being converted.
16 | /// The being used.
17 | ///
18 | /// The value that was converted.
19 | ///
20 | ///
21 | /// A converter may throw any Exception, but should throw JsonException when the JSON is invalid.
22 | ///
23 | public override ConcurrencyToken Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
24 | => new(reader.GetString());
25 |
26 | ///
27 | /// Write the value as JSON.
28 | ///
29 | /// The to write to.
30 | /// The value to convert. Note that the value of determines if the converter handles values.
31 | /// The being used.
32 | ///
33 | /// A converter may throw any Exception, but should throw JsonException when the JSON
34 | /// cannot be created.
35 | ///
36 | public override void Write(Utf8JsonWriter writer, ConcurrencyToken value, JsonSerializerOptions options)
37 | => writer.WriteStringValue(value.ToString());
38 | }
39 |
--------------------------------------------------------------------------------
/src/FluentCommand.Json/FluentCommand.Json.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netstandard2.0;net8.0;net9.0
4 | FluentCommand
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/FluentCommand.SqlServer/Bulk/DataBulkCopyExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Data.SqlClient;
2 |
3 | namespace FluentCommand.Bulk;
4 |
5 | ///
6 | /// Bulk Copy extension methods
7 | ///
8 | public static class DataBulkCopyExtensions
9 | {
10 | ///
11 | /// Starts a data bulk-copy with the specified destination table name.
12 | ///
13 | /// The session to use for the bulk-copy.
14 | /// Name of the destination table on the server.
15 | ///
16 | /// A fluent to a operation.
17 | ///
18 | public static IDataBulkCopy BulkCopy(this IDataSession session, string destinationTable)
19 | {
20 | var bulkCopy = new DataBulkCopy(session, destinationTable);
21 | return bulkCopy;
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/src/FluentCommand.SqlServer/DataConfigurationBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Data.SqlClient;
2 |
3 | namespace FluentCommand;
4 |
5 | public static class DataConfigurationBuilderExtensions
6 | {
7 | public static DataConfigurationBuilder UseSqlServer(this DataConfigurationBuilder builder)
8 | {
9 | builder
10 | .AddProviderFactory(SqlClientFactory.Instance)
11 | .AddSqlServerGenerator();
12 |
13 | return builder;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/FluentCommand.SqlServer/FluentCommand.SqlServer.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net462;net8.0;net9.0
4 | FluentCommand
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/FluentCommand.SqlServer/Import/FieldDefault.cs:
--------------------------------------------------------------------------------
1 | namespace FluentCommand.Import;
2 |
3 | ///
4 | /// Default field value
5 | ///
6 | public enum FieldDefault
7 | {
8 | ///
9 | /// Use the current username as the default value.
10 | ///
11 | UserName,
12 | ///
13 | /// Use the current UTC date time as the default value.
14 | ///
15 | CurrentDate,
16 | ///
17 | /// Use a static default value.
18 | ///
19 | Static
20 | }
--------------------------------------------------------------------------------
/src/FluentCommand.SqlServer/Import/FieldMap.cs:
--------------------------------------------------------------------------------
1 | namespace FluentCommand.Import;
2 |
3 | ///
4 | /// Import field mapping
5 | ///
6 | public class FieldMap
7 | {
8 | ///
9 | /// Gets or sets the field index.
10 | ///
11 | ///
12 | /// The field index.
13 | ///
14 | public int? Index { get; set; }
15 |
16 | ///
17 | /// Gets or sets the name of the field.
18 | ///
19 | ///
20 | /// The name of the field.
21 | ///
22 | public string Name { get; set; }
23 |
24 | ///
25 | /// Returns a that represents this instance.
26 | ///
27 | ///
28 | /// A that represents this instance.
29 | ///
30 | public override string ToString()
31 | {
32 | return $"Name: {Name}, Index: {Index}";
33 | }
34 | }
--------------------------------------------------------------------------------
/src/FluentCommand.SqlServer/Import/IFieldTranslator.cs:
--------------------------------------------------------------------------------
1 | namespace FluentCommand.Import;
2 |
3 | ///
4 | /// An interface for translating a field value
5 | ///
6 | public interface IFieldTranslator
7 | {
8 | ///
9 | /// Translates the specified original value.
10 | ///
11 | /// The original value.
12 | /// The translated value
13 | Task