Skip to content

Commit 84f0282

Browse files
authored
Merge pull request #13 from cajuncoding/feature/add_support_to_manually_set_identity_values_on_insert
Restored support for SqlConnection Factory (simplified now as a Func&…
2 parents 5530fcd + 1b57d20 commit 84f0282

File tree

7 files changed

+273
-28
lines changed

7 files changed

+273
-28
lines changed

NetStandard.SqlBulkHelpers/CustomExtensions/SqlBulkHelpersCustomExtensions.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,15 @@ public static async Task<SqlConnection> EnsureSqlConnectionIsOpenAsync(this SqlC
3232
return sqlConnection;
3333
}
3434

35+
public static SqlConnection EnsureSqlConnectionIsOpen(this SqlConnection sqlConnection)
36+
{
37+
sqlConnection.AssertArgumentIsNotNull(nameof(sqlConnection));
38+
if (sqlConnection.State != ConnectionState.Open)
39+
sqlConnection.Open();
40+
41+
return sqlConnection;
42+
}
43+
3544
public static TableNameTerm GetSqlBulkHelpersMappedTableNameTerm(this Type type, string tableNameOverride = null)
3645
{
3746
string tableName = tableNameOverride;

NetStandard.SqlBulkHelpers/Database/SqlBulkHelpersDBSchemaLoader.Async.cs

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,51 @@ public async Task<SqlBulkHelpersTableDefinition> GetTableSchemaDefinitionAsync(
8080
{
8181
sqlConnection.AssertArgumentIsNotNull(nameof(sqlConnection));
8282

83+
var tableDefinition = await GetTableSchemaDefinitionInternalAsync(
84+
tableName,
85+
detailLevel,
86+
() => Task.FromResult((sqlConnection, sqlTransaction)),
87+
disposeOfConnection: false, //DO Not dispose of Existing Connection/Transaction...
88+
forceCacheReload
89+
).ConfigureAwait(false);
90+
91+
return tableDefinition;
92+
}
93+
94+
public async Task<SqlBulkHelpersTableDefinition> GetTableSchemaDefinitionAsync(
95+
string tableName,
96+
TableSchemaDetailLevel detailLevel,
97+
Func<Task<SqlConnection>> sqlConnectionAsyncFactory,
98+
bool forceCacheReload = false
99+
)
100+
{
101+
sqlConnectionAsyncFactory.AssertArgumentIsNotNull(nameof(sqlConnectionAsyncFactory));
102+
103+
var tableDefinition = await GetTableSchemaDefinitionInternalAsync(
104+
tableName,
105+
detailLevel,
106+
async () =>
107+
{
108+
var sqlConnection = await sqlConnectionAsyncFactory().ConfigureAwait(false);
109+
return (sqlConnection, (SqlTransaction)null);
110+
},
111+
disposeOfConnection: true, //Always DISPOSE of New Connections created by the Factory...
112+
forceCacheReload
113+
).ConfigureAwait(false);
114+
115+
return tableDefinition;
116+
}
117+
118+
protected async Task<SqlBulkHelpersTableDefinition> GetTableSchemaDefinitionInternalAsync(
119+
string tableName,
120+
TableSchemaDetailLevel detailLevel,
121+
Func<Task<(SqlConnection, SqlTransaction)>> sqlConnectionAndTransactionAsyncFactory,
122+
bool disposeOfConnection,
123+
bool forceCacheReload = false
124+
)
125+
{
126+
sqlConnectionAndTransactionAsyncFactory.AssertArgumentIsNotNull(nameof(sqlConnectionAndTransactionAsyncFactory));
127+
83128
if (string.IsNullOrWhiteSpace(tableName))
84129
return null;
85130

@@ -93,16 +138,37 @@ public async Task<SqlBulkHelpersTableDefinition> GetTableSchemaDefinitionAsync(
93138
key: cacheKey,
94139
cacheValueFactoryAsync: async key =>
95140
{
96-
using (var sqlCmd = CreateSchemaQuerySqlCommand(tableNameTerm, detailLevel, sqlConnection, sqlTransaction))
141+
var (sqlConnection, sqlTransaction) = await sqlConnectionAndTransactionAsyncFactory().ConfigureAwait(false);
142+
try
97143
{
98-
//Execute and load results from the Json...
99-
var tableDef = await sqlCmd.ExecuteForJsonAsync<SqlBulkHelpersTableDefinition>().ConfigureAwait(false);
100-
return tableDef;
144+
//If we don't have a Transaction then offer lazy opening of the Connection,
145+
// but if we do have a Transaction we assume the Connection is open & valid for the Transaction...
146+
if (sqlTransaction == null)
147+
await sqlConnection.EnsureSqlConnectionIsOpenAsync().ConfigureAwait(false);
148+
149+
using (var sqlCmd = CreateSchemaQuerySqlCommand(tableNameTerm, detailLevel, sqlConnection, sqlTransaction))
150+
{
151+
//Execute and load results from the Json...
152+
var tableDef = await sqlCmd.ExecuteForJsonAsync<SqlBulkHelpersTableDefinition>().ConfigureAwait(false);
153+
return tableDef;
154+
}
155+
156+
}
157+
finally
158+
{
159+
#if NETSTANDARD2_1
160+
if(disposeOfConnection)
161+
await sqlConnection.DisposeAsync().ConfigureAwait(false);
162+
#else
163+
if(disposeOfConnection)
164+
sqlConnection.Dispose();
165+
#endif
101166
}
102167
}
103168
).ConfigureAwait(false);
104169

105170
return tableDefinition;
106171
}
172+
107173
}
108174
}

NetStandard.SqlBulkHelpers/Database/SqlBulkHelpersDBSchemaLoader.Sync.cs

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,48 @@ public SqlBulkHelpersTableDefinition GetTableSchemaDefinition(
2525
bool forceCacheReload = false
2626
)
2727
{
28-
sqlConnection.AssertArgumentIsNotNull(nameof(sqlConnection));
28+
sqlConnection.AssertArgumentIsNotNull(nameof(sqlConnection));
29+
30+
var tableDefinition = GetTableSchemaDefinitionInternal(
31+
tableName,
32+
detailLevel,
33+
() => (sqlConnection, sqlTransaction),
34+
disposeOfConnection: false, //DO Not dispose of Existing Connection/Transaction...
35+
forceCacheReload
36+
);
37+
38+
return tableDefinition;
39+
}
40+
41+
public SqlBulkHelpersTableDefinition GetTableSchemaDefinition(
42+
string tableName,
43+
TableSchemaDetailLevel detailLevel,
44+
Func<SqlConnection> sqlConnectionFactory,
45+
bool forceCacheReload = false
46+
)
47+
{
48+
sqlConnectionFactory.AssertArgumentIsNotNull(nameof(sqlConnectionFactory));
49+
50+
var tableDefinition = GetTableSchemaDefinitionInternal(
51+
tableName,
52+
detailLevel,
53+
() => (sqlConnectionFactory(), (SqlTransaction)null),
54+
disposeOfConnection: true, //Always DISPOSE of New Connections created by the Factory...
55+
forceCacheReload
56+
);
57+
58+
return tableDefinition;
59+
}
60+
61+
protected SqlBulkHelpersTableDefinition GetTableSchemaDefinitionInternal(
62+
string tableName,
63+
TableSchemaDetailLevel detailLevel,
64+
Func<(SqlConnection, SqlTransaction)> sqlConnectionAndTransactionFactory,
65+
bool disposeOfConnection,
66+
bool forceCacheReload = false
67+
)
68+
{
69+
sqlConnectionAndTransactionFactory.AssertArgumentIsNotNull(nameof(sqlConnectionAndTransactionFactory));
2970

3071
if (string.IsNullOrWhiteSpace(tableName))
3172
return null;
@@ -40,11 +81,25 @@ public SqlBulkHelpersTableDefinition GetTableSchemaDefinition(
4081
key: cacheKey,
4182
cacheValueFactory: key =>
4283
{
43-
using (var sqlCmd = CreateSchemaQuerySqlCommand(tableNameTerm, detailLevel, sqlConnection, sqlTransaction))
84+
var (sqlConnection, sqlTransaction) = sqlConnectionAndTransactionFactory();
85+
try
86+
{
87+
//If we don't have a Transaction then offer lazy opening of the Connection,
88+
// but if we do have a Transaction we assume the Connection is open & valid for the Transaction...
89+
if (sqlTransaction == null)
90+
sqlConnection.EnsureSqlConnectionIsOpen();
91+
92+
using (var sqlCmd = CreateSchemaQuerySqlCommand(tableNameTerm, detailLevel, sqlConnection, sqlTransaction))
93+
{
94+
//Execute and load results from the Json...
95+
var tableDef = sqlCmd.ExecuteForJson<SqlBulkHelpersTableDefinition>();
96+
return tableDef;
97+
}
98+
}
99+
finally
44100
{
45-
//Execute and load results from the Json...
46-
var tableDef = sqlCmd.ExecuteForJson<SqlBulkHelpersTableDefinition>();
47-
return tableDef;
101+
if(disposeOfConnection)
102+
sqlConnection.Dispose();
48103
}
49104
});
50105

NetStandard.SqlBulkHelpers/NetStandard.SqlBulkHelpers.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<Description>A library for easy, efficient and high performance bulk insert and update of data, into a Sql Database, from .Net applications. By leveraging the power of the SqlBulkCopy classes with added support for Identity primary key table columns this library provides a greatly simplified interface to process Identity based Entities with Bulk Performance with the wide compatibility of .NetStandard 2.0.</Description>
1414
<PackageTags>sql server database table bulk insert update identity column sqlbulkcopy orm dapper linq2sql materialization materialized data view materialized-data materialized-view sync replication replica readonly</PackageTags>
1515
<PackageReleaseNotes>
16+
- Restored support for SqlConnection Factory (simplified now as a Func&lt;SqlConnection&gt; when manually using the SqlDbSchemaLoader to dynamically retrieve Table Schema definitions for performance.
1617
- Added support for other Identity column data types including (INT, BIGINT, SMALLINT, &amp; TINYINT); per feature request (https://github.com/cajuncoding/SqlBulkHelpers/issues/10).
1718
- Added support to explicitly set Identity Values (aka SET IDENTITY_INSERT ON) via new `enableIdentityInsert` api parameter.
1819
- Added support to retreive and re-seed (aka set) the current Identity Value on a given table via new apis in the MaterializedData helpers.
@@ -47,7 +48,7 @@
4748
- Added more Integration Tests for Constructors and Connections, as well as the new DB Schema Loader caching implementation.
4849
- Fixed bug in dynamic initialization of SqlBulkHelpersConnectionProvider and SqlBulkHelpersDBSchemaLoader when not using the Default instances that automtically load the connection string from the application configuration setting.
4950
</PackageReleaseNotes>
50-
<Version>2.2</Version>
51+
<Version>2.2.1</Version>
5152
</PropertyGroup>
5253

5354
<ItemGroup>

NetStandard.SqlBulkHelpers/SqlBulkHelper/Interfaces/ISqlBulkHelpersDBSchemaLoader.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,30 @@ Task<SqlBulkHelpersTableDefinition> GetTableSchemaDefinitionAsync(
1414
bool forceCacheReload = false
1515
);
1616

17-
ValueTask ClearCacheAsync();
17+
Task<SqlBulkHelpersTableDefinition> GetTableSchemaDefinitionAsync(
18+
string tableName,
19+
TableSchemaDetailLevel detailLevel,
20+
Func<Task<SqlConnection>> sqlConnectionAsyncFactory,
21+
bool forceCacheReload = false
22+
);
1823

1924
SqlBulkHelpersTableDefinition GetTableSchemaDefinition(
20-
string tableName,
25+
string tableName,
2126
TableSchemaDetailLevel detailLevel,
2227
SqlConnection sqlConnection,
2328
SqlTransaction sqlTransaction = null,
2429
bool forceCacheReload = false
2530
);
2631

32+
SqlBulkHelpersTableDefinition GetTableSchemaDefinition(
33+
string tableName,
34+
TableSchemaDetailLevel detailLevel,
35+
Func<SqlConnection> sqlConnectionFactory,
36+
bool forceCacheReload = false
37+
);
38+
39+
ValueTask ClearCacheAsync();
40+
2741
void ClearCache();
2842
}
2943
}

README.md

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,9 @@ public class TestDataService
215215
## Nuget Package
216216
To use in your project, add the [SqlBulkHelpers NuGet package](https://www.nuget.org/packages/SqlBulkHelpers/) to your project.
217217

218+
## v2.2.1 Release Notes:
219+
- Restored support for SqlConnection Factory (simplified now as a Func&lt;SqlConnection&gt; when manually using the SqlDbSchemaLoader to dynamically retrieve Table Schema definitions for performance.
220+
218221
## v2.2 Release Notes:
219222
- Added support for other Identity column data types including (INT, BIGINT, SMALLINT, & TINYINT); per [feature request here](https://github.com/cajuncoding/SqlBulkHelpers/issues/10).
220223
- Added support to explicitly set Identity Values (aka SET IDENTITY_INSERT ON) via new `enableIdentityInsert` api parameter.
@@ -334,26 +337,29 @@ It offers ability to retrieve basic or extended details; both of which are inter
334337

335338
*NOTE: The internal schema caching can be invalidated using the `forceCacheReload` method parameter.*
336339

340+
NOTE: You man use an existing SqlConnection and/or SqlTransaction with this api, however for maximum performance it's recommended to
341+
use a SqlConnection Factory Func so connections are not created at all if the results are already cached...
337342
```csharp
343+
//Normally would be provided by Dependency Injection...
344+
//This is a DI friendly connection factory/provider pattern that can be used...
345+
private readonly ISqlBulkHelpersConnectionProvider _sqlConnectionProvider = new SqlBulkHelpersConnectionProvider(sqlConnectionString);
346+
338347
public async Task<string> GetSanitizedTableName(string tableNameToValidate)
339348
{
340-
//Normally would be provided by Dependency Injection...
341-
//This is a DI friendly connection factory/provider pattern that can be used...
342-
ISqlBulkHelpersConnectionProvider sqlConnectionProvider = new SqlBulkHelpersConnectionProvider(sqlConnectionString);
343-
344-
using (SqlConnection sqlConnection = await sqlConnectionProvider.NewConnectionAsync())
345-
{
346-
//We can get the basic or extended (slower query) schema details for the table (both types are cached)...
347-
//NOTE: Basic details includes table name, columns, data types, etc. while Extended details includes FKey constraintes,
348-
// Indexes, relationship details, etc.
349-
//NOTE: This is cached, so no DB call is made if it's already been loaded and the forceCacheReload flag is not set to true.
350-
var tableDefinition = await sqlConnection.GetTableSchemaDefinitionAsync(tablNameToValidate, TableSchemaDetailLevel.BasicDetails)
349+
//We can get the basic or extended (slower query) schema details for the table (both types are cached)...
350+
//NOTE: Basic details includes table name, columns, data types, etc. while Extended details includes FKey constraintes,
351+
// Indexes, relationship details, etc.
352+
//NOTE: This is cached, so no DB call is made if it's already been loaded and the forceCacheReload flag is not set to true.
353+
var tableDefinition = await sqlConnection.GetTableSchemaDefinitionAsync(
354+
tablNameToValidate,
355+
TableSchemaDetailLevel.BasicDetails
356+
async () => await _sqlConnectionProvider.NewConnectionAsync()
357+
);
351358

352-
if (tableDefinition == null)
353-
throw new NullReferenceException($"The Table Definition is null and could not be found for the table name specified [{tableNameToValidate}].");
359+
if (tableDefinition == null)
360+
throw new NullReferenceException($"The Table Definition is null and could not be found for the table name specified [{tableNameToValidate}].");
354361

355-
return tableDefinition.TableFullyQualifiedName;
356-
}
362+
return tableDefinition.TableFullyQualifiedName;
357363
}
358364
```
359365

0 commit comments

Comments
 (0)