Skip to content

Commit

Permalink
Merge pull request #13 from cajuncoding/feature/add_support_to_manual…
Browse files Browse the repository at this point in the history
…ly_set_identity_values_on_insert

Restored support for SqlConnection Factory (simplified now as a Func&…
  • Loading branch information
cajuncoding committed May 25, 2023
2 parents 5530fcd + 1b57d20 commit 84f0282
Show file tree
Hide file tree
Showing 7 changed files with 273 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@ public static async Task<SqlConnection> EnsureSqlConnectionIsOpenAsync(this SqlC
return sqlConnection;
}

public static SqlConnection EnsureSqlConnectionIsOpen(this SqlConnection sqlConnection)
{
sqlConnection.AssertArgumentIsNotNull(nameof(sqlConnection));
if (sqlConnection.State != ConnectionState.Open)
sqlConnection.Open();

return sqlConnection;
}

public static TableNameTerm GetSqlBulkHelpersMappedTableNameTerm(this Type type, string tableNameOverride = null)
{
string tableName = tableNameOverride;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,51 @@ protected string GetTableSchemaSqlQuery(TableSchemaDetailLevel detailLevel)
{
sqlConnection.AssertArgumentIsNotNull(nameof(sqlConnection));

var tableDefinition = await GetTableSchemaDefinitionInternalAsync(
tableName,
detailLevel,
() => Task.FromResult((sqlConnection, sqlTransaction)),
disposeOfConnection: false, //DO Not dispose of Existing Connection/Transaction...
forceCacheReload
).ConfigureAwait(false);

return tableDefinition;
}

public async Task<SqlBulkHelpersTableDefinition> GetTableSchemaDefinitionAsync(
string tableName,
TableSchemaDetailLevel detailLevel,
Func<Task<SqlConnection>> sqlConnectionAsyncFactory,
bool forceCacheReload = false
)
{
sqlConnectionAsyncFactory.AssertArgumentIsNotNull(nameof(sqlConnectionAsyncFactory));

var tableDefinition = await GetTableSchemaDefinitionInternalAsync(
tableName,
detailLevel,
async () =>
{
var sqlConnection = await sqlConnectionAsyncFactory().ConfigureAwait(false);
return (sqlConnection, (SqlTransaction)null);
},
disposeOfConnection: true, //Always DISPOSE of New Connections created by the Factory...
forceCacheReload
).ConfigureAwait(false);

return tableDefinition;
}

protected async Task<SqlBulkHelpersTableDefinition> GetTableSchemaDefinitionInternalAsync(
string tableName,
TableSchemaDetailLevel detailLevel,
Func<Task<(SqlConnection, SqlTransaction)>> sqlConnectionAndTransactionAsyncFactory,
bool disposeOfConnection,
bool forceCacheReload = false
)
{
sqlConnectionAndTransactionAsyncFactory.AssertArgumentIsNotNull(nameof(sqlConnectionAndTransactionAsyncFactory));

if (string.IsNullOrWhiteSpace(tableName))
return null;

Expand All @@ -93,16 +138,37 @@ protected string GetTableSchemaSqlQuery(TableSchemaDetailLevel detailLevel)
key: cacheKey,
cacheValueFactoryAsync: async key =>
{
using (var sqlCmd = CreateSchemaQuerySqlCommand(tableNameTerm, detailLevel, sqlConnection, sqlTransaction))
var (sqlConnection, sqlTransaction) = await sqlConnectionAndTransactionAsyncFactory().ConfigureAwait(false);
try
{
//Execute and load results from the Json...
var tableDef = await sqlCmd.ExecuteForJsonAsync<SqlBulkHelpersTableDefinition>().ConfigureAwait(false);
return tableDef;
//If we don't have a Transaction then offer lazy opening of the Connection,
// but if we do have a Transaction we assume the Connection is open & valid for the Transaction...
if (sqlTransaction == null)
await sqlConnection.EnsureSqlConnectionIsOpenAsync().ConfigureAwait(false);
using (var sqlCmd = CreateSchemaQuerySqlCommand(tableNameTerm, detailLevel, sqlConnection, sqlTransaction))
{
//Execute and load results from the Json...
var tableDef = await sqlCmd.ExecuteForJsonAsync<SqlBulkHelpersTableDefinition>().ConfigureAwait(false);
return tableDef;
}
}
finally
{
#if NETSTANDARD2_1
if(disposeOfConnection)
await sqlConnection.DisposeAsync().ConfigureAwait(false);
#else
if(disposeOfConnection)
sqlConnection.Dispose();
#endif
}
}
).ConfigureAwait(false);

return tableDefinition;
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,48 @@ public partial class SqlBulkHelpersDBSchemaLoader : ISqlBulkHelpersDBSchemaLoade
bool forceCacheReload = false
)
{
sqlConnection.AssertArgumentIsNotNull(nameof(sqlConnection));
sqlConnection.AssertArgumentIsNotNull(nameof(sqlConnection));

var tableDefinition = GetTableSchemaDefinitionInternal(
tableName,
detailLevel,
() => (sqlConnection, sqlTransaction),
disposeOfConnection: false, //DO Not dispose of Existing Connection/Transaction...
forceCacheReload
);

return tableDefinition;
}

public SqlBulkHelpersTableDefinition GetTableSchemaDefinition(
string tableName,
TableSchemaDetailLevel detailLevel,
Func<SqlConnection> sqlConnectionFactory,
bool forceCacheReload = false
)
{
sqlConnectionFactory.AssertArgumentIsNotNull(nameof(sqlConnectionFactory));

var tableDefinition = GetTableSchemaDefinitionInternal(
tableName,
detailLevel,
() => (sqlConnectionFactory(), (SqlTransaction)null),
disposeOfConnection: true, //Always DISPOSE of New Connections created by the Factory...
forceCacheReload
);

return tableDefinition;
}

protected SqlBulkHelpersTableDefinition GetTableSchemaDefinitionInternal(
string tableName,
TableSchemaDetailLevel detailLevel,
Func<(SqlConnection, SqlTransaction)> sqlConnectionAndTransactionFactory,
bool disposeOfConnection,
bool forceCacheReload = false
)
{
sqlConnectionAndTransactionFactory.AssertArgumentIsNotNull(nameof(sqlConnectionAndTransactionFactory));

if (string.IsNullOrWhiteSpace(tableName))
return null;
Expand All @@ -40,11 +81,25 @@ public partial class SqlBulkHelpersDBSchemaLoader : ISqlBulkHelpersDBSchemaLoade
key: cacheKey,
cacheValueFactory: key =>
{
using (var sqlCmd = CreateSchemaQuerySqlCommand(tableNameTerm, detailLevel, sqlConnection, sqlTransaction))
var (sqlConnection, sqlTransaction) = sqlConnectionAndTransactionFactory();
try
{
//If we don't have a Transaction then offer lazy opening of the Connection,
// but if we do have a Transaction we assume the Connection is open & valid for the Transaction...
if (sqlTransaction == null)
sqlConnection.EnsureSqlConnectionIsOpen();
using (var sqlCmd = CreateSchemaQuerySqlCommand(tableNameTerm, detailLevel, sqlConnection, sqlTransaction))
{
//Execute and load results from the Json...
var tableDef = sqlCmd.ExecuteForJson<SqlBulkHelpersTableDefinition>();
return tableDef;
}
}
finally
{
//Execute and load results from the Json...
var tableDef = sqlCmd.ExecuteForJson<SqlBulkHelpersTableDefinition>();
return tableDef;
if(disposeOfConnection)
sqlConnection.Dispose();
}
});

Expand Down
3 changes: 2 additions & 1 deletion NetStandard.SqlBulkHelpers/NetStandard.SqlBulkHelpers.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<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>
<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>
<PackageReleaseNotes>
- 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.
- Added support for other Identity column data types including (INT, BIGINT, SMALLINT, &amp; TINYINT); per feature request (https://github.com/cajuncoding/SqlBulkHelpers/issues/10).
- Added support to explicitly set Identity Values (aka SET IDENTITY_INSERT ON) via new `enableIdentityInsert` api parameter.
- Added support to retreive and re-seed (aka set) the current Identity Value on a given table via new apis in the MaterializedData helpers.
Expand Down Expand Up @@ -47,7 +48,7 @@
- Added more Integration Tests for Constructors and Connections, as well as the new DB Schema Loader caching implementation.
- 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.
</PackageReleaseNotes>
<Version>2.2</Version>
<Version>2.2.1</Version>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,30 @@ public interface ISqlBulkHelpersDBSchemaLoader
bool forceCacheReload = false
);

ValueTask ClearCacheAsync();
Task<SqlBulkHelpersTableDefinition> GetTableSchemaDefinitionAsync(
string tableName,
TableSchemaDetailLevel detailLevel,
Func<Task<SqlConnection>> sqlConnectionAsyncFactory,
bool forceCacheReload = false
);

SqlBulkHelpersTableDefinition GetTableSchemaDefinition(
string tableName,
string tableName,
TableSchemaDetailLevel detailLevel,
SqlConnection sqlConnection,
SqlTransaction sqlTransaction = null,
bool forceCacheReload = false
);

SqlBulkHelpersTableDefinition GetTableSchemaDefinition(
string tableName,
TableSchemaDetailLevel detailLevel,
Func<SqlConnection> sqlConnectionFactory,
bool forceCacheReload = false
);

ValueTask ClearCacheAsync();

void ClearCache();
}
}
36 changes: 21 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,9 @@ public class TestDataService
## Nuget Package
To use in your project, add the [SqlBulkHelpers NuGet package](https://www.nuget.org/packages/SqlBulkHelpers/) to your project.

## v2.2.1 Release Notes:
- 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.

## v2.2 Release Notes:
- Added support for other Identity column data types including (INT, BIGINT, SMALLINT, & TINYINT); per [feature request here](https://github.com/cajuncoding/SqlBulkHelpers/issues/10).
- Added support to explicitly set Identity Values (aka SET IDENTITY_INSERT ON) via new `enableIdentityInsert` api parameter.
Expand Down Expand Up @@ -334,26 +337,29 @@ It offers ability to retrieve basic or extended details; both of which are inter

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

NOTE: You man use an existing SqlConnection and/or SqlTransaction with this api, however for maximum performance it's recommended to
use a SqlConnection Factory Func so connections are not created at all if the results are already cached...
```csharp
//Normally would be provided by Dependency Injection...
//This is a DI friendly connection factory/provider pattern that can be used...
private readonly ISqlBulkHelpersConnectionProvider _sqlConnectionProvider = new SqlBulkHelpersConnectionProvider(sqlConnectionString);

public async Task<string> GetSanitizedTableName(string tableNameToValidate)
{
//Normally would be provided by Dependency Injection...
//This is a DI friendly connection factory/provider pattern that can be used...
ISqlBulkHelpersConnectionProvider sqlConnectionProvider = new SqlBulkHelpersConnectionProvider(sqlConnectionString);

using (SqlConnection sqlConnection = await sqlConnectionProvider.NewConnectionAsync())
{
//We can get the basic or extended (slower query) schema details for the table (both types are cached)...
//NOTE: Basic details includes table name, columns, data types, etc. while Extended details includes FKey constraintes,
// Indexes, relationship details, etc.
//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.
var tableDefinition = await sqlConnection.GetTableSchemaDefinitionAsync(tablNameToValidate, TableSchemaDetailLevel.BasicDetails)
//We can get the basic or extended (slower query) schema details for the table (both types are cached)...
//NOTE: Basic details includes table name, columns, data types, etc. while Extended details includes FKey constraintes,
// Indexes, relationship details, etc.
//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.
var tableDefinition = await sqlConnection.GetTableSchemaDefinitionAsync(
tablNameToValidate,
TableSchemaDetailLevel.BasicDetails
async () => await _sqlConnectionProvider.NewConnectionAsync()
);

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

return tableDefinition.TableFullyQualifiedName;
}
return tableDefinition.TableFullyQualifiedName;
}
```

Expand Down

0 comments on commit 84f0282

Please sign in to comment.