forked from MarimerLLC/csla
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
MarimerLLC#981 Move NS2 ConnectionManager to new project/namespace; s…
…plit out SafeSqlDataReader functionality for NS2, leaving old implementation for .NET FX
- Loading branch information
1 parent
000d897
commit 8ab18c7
Showing
7 changed files
with
480 additions
and
92 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
//----------------------------------------------------------------------- | ||
// <copyright file="ConnectionManager.cs" company="Marimer LLC"> | ||
// Copyright (c) Marimer LLC. All rights reserved. | ||
// Website: https://cslanet.com | ||
// </copyright> | ||
// <summary>Provides an automated way to reuse open</summary> | ||
//----------------------------------------------------------------------- | ||
using System; | ||
using Csla.Configuration; | ||
using System.Data; | ||
using System.Data.SqlClient; | ||
using Csla.Properties; | ||
|
||
namespace Csla.Data.SqlClient | ||
{ | ||
/// <summary> | ||
/// Provides an automated way to reuse open | ||
/// database connections within the context | ||
/// of a single data portal operation. | ||
/// </summary> | ||
/// <remarks> | ||
/// This type stores the open database connection | ||
/// in <see cref="Csla.ApplicationContext.LocalContext" /> | ||
/// and uses reference counting through | ||
/// <see cref="IDisposable" /> to keep the connection | ||
/// open for reuse by child objects, and to automatically | ||
/// dispose the connection when the last consumer | ||
/// has called Dispose." | ||
/// </remarks> | ||
public class ConnectionManager : IDisposable | ||
{ | ||
private static readonly object _lock = new object(); | ||
private readonly string _connectionString; | ||
private readonly string _label; | ||
|
||
/// <summary> | ||
/// Gets the ConnectionManager object for the | ||
/// specified database. | ||
/// </summary> | ||
/// <param name="database"> | ||
/// Database name as shown in the config file. | ||
/// </param> | ||
public static ConnectionManager GetManager(string database) | ||
{ | ||
return GetManager(database, true); | ||
} | ||
|
||
/// <summary> | ||
/// Gets the ConnectionManager object for the | ||
/// specified database. | ||
/// </summary> | ||
/// <param name="database"> | ||
/// Database name as shown in the config file. | ||
/// </param> | ||
/// <param name="label">Label for this connection.</param> | ||
public static ConnectionManager GetManager(string database, string label) | ||
{ | ||
return GetManager(database, true, label); | ||
} | ||
|
||
/// <summary> | ||
/// Gets the ConnectionManager object for the | ||
/// specified database. | ||
/// </summary> | ||
/// <param name="database"> | ||
/// The database name or connection string. | ||
/// </param> | ||
/// <param name="isDatabaseName"> | ||
/// True to indicate that the connection string | ||
/// should be retrieved from the config file. If | ||
/// False, the database parameter is directly | ||
/// used as a connection string. | ||
/// </param> | ||
/// <returns>ConnectionManager object for the name.</returns> | ||
public static ConnectionManager GetManager(string database, bool isDatabaseName) | ||
{ | ||
return GetManager(database, isDatabaseName, "default"); | ||
} | ||
|
||
/// <summary> | ||
/// Gets the ConnectionManager object for the | ||
/// specified database. | ||
/// </summary> | ||
/// <param name="database"> | ||
/// The database name or connection string. | ||
/// </param> | ||
/// <param name="isDatabaseName"> | ||
/// True to indicate that the connection string | ||
/// should be retrieved from the config file. If | ||
/// False, the database parameter is directly | ||
/// used as a connection string. | ||
/// </param> | ||
/// <param name="label">Label for this connection.</param> | ||
/// <returns>ConnectionManager object for the name.</returns> | ||
public static ConnectionManager GetManager(string database, bool isDatabaseName, string label) | ||
{ | ||
if (isDatabaseName) | ||
{ | ||
var connection = ConfigurationManager.ConnectionStrings[database]; | ||
if (connection == null) | ||
throw new ConfigurationErrorsException(String.Format(Resources.DatabaseNameNotFound, database)); | ||
|
||
var conn = ConfigurationManager.ConnectionStrings[database].ConnectionString; | ||
if (string.IsNullOrEmpty(conn)) | ||
throw new ConfigurationErrorsException(String.Format(Resources.DatabaseNameNotFound, database)); | ||
database = conn; | ||
} | ||
|
||
ConnectionManager mgr = null; | ||
var ctxName = GetContextName(database, label); | ||
lock (_lock) | ||
{ | ||
var cached = ApplicationContext.LocalContext.GetValueOrNull(ctxName); | ||
if (cached != null) | ||
{ | ||
mgr = (ConnectionManager)cached; | ||
mgr.AddRef(); | ||
} | ||
} | ||
|
||
if (mgr == null) | ||
{ | ||
mgr = new ConnectionManager(database, label); | ||
lock (_lock) | ||
{ | ||
ApplicationContext.LocalContext[ctxName] = mgr; | ||
mgr.AddRef(); | ||
} | ||
} | ||
return mgr; | ||
} | ||
|
||
private ConnectionManager(string connectionString, string label) | ||
{ | ||
_label = label; | ||
_connectionString = connectionString; | ||
|
||
Connection = new SqlConnection(connectionString); | ||
Connection.Open(); | ||
} | ||
|
||
private static string GetContextName(string connectionString, string label) | ||
{ | ||
return "__db:" + label + "-" + connectionString; | ||
} | ||
|
||
/// <summary> | ||
/// Dispose object, dereferencing or | ||
/// disposing the connection it is | ||
/// managing. | ||
/// </summary> | ||
public IDbConnection Connection { get; } | ||
|
||
/// <summary> | ||
/// Gets the current reference count for this | ||
/// object. | ||
/// </summary> | ||
public int RefCount { get; private set; } | ||
|
||
private void AddRef() | ||
{ | ||
RefCount += 1; | ||
} | ||
|
||
private void DeRef() | ||
{ | ||
RefCount -= 1; | ||
if (RefCount == 0) | ||
{ | ||
Connection.Close(); | ||
Connection.Dispose(); | ||
lock (_lock) | ||
ApplicationContext.LocalContext.Remove(GetContextName(_connectionString, _label)); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Dispose object, dereferencing or | ||
/// disposing the connection it is | ||
/// managing. | ||
/// </summary> | ||
public void Dispose() | ||
{ | ||
DeRef(); | ||
} | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
Source/Csla.Data.SqlClient.Fx/Csla.Data.SqlClient.Fx.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>netstandard2.0</TargetFramework> | ||
<RootNamespace>Csla.Data.SqlClient</RootNamespace> | ||
</PropertyGroup> | ||
|
||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> | ||
<DefineConstants>TRACE;NETSTANDARD2_0</DefineConstants> | ||
</PropertyGroup> | ||
|
||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> | ||
<DefineConstants>TRACE;NETSTANDARD2_0</DefineConstants> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="System.Data.SqlClient" Version="4.6.1" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\Csla.NetStandard2.0\Csla.NetStandard2.0.csproj" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
//----------------------------------------------------------------------- | ||
// <copyright file="SafeSqlDataReader.cs" company="Marimer LLC"> | ||
// Copyright (c) Marimer LLC. All rights reserved. | ||
// Website: https://cslanet.com | ||
// </copyright> | ||
// <summary>This is a SqlDataReader based on SafeDataReader</summary> | ||
//----------------------------------------------------------------------- | ||
using System; | ||
using System.Data; | ||
using System.Data.SqlClient; | ||
using System.Threading.Tasks; | ||
|
||
namespace Csla.Data.SqlClient | ||
{ | ||
/// <summary> | ||
/// This is a SqlDataReader that 'fixes' any null values before | ||
/// they are returned to our business code. | ||
/// </summary> | ||
public class SafeSqlDataReader : SafeDataReader | ||
{ | ||
/// <summary> | ||
/// Get a reference to the underlying | ||
/// SqlDataReader if present. | ||
/// </summary> | ||
public SqlDataReader SqlDataReader { get; } | ||
|
||
/// <summary> | ||
/// Initializes the SafeDataReader object to use data from | ||
/// the provided DataReader object. | ||
/// </summary> | ||
/// <param name="dataReader">The source DataReader object containing the data.</param> | ||
public SafeSqlDataReader(IDataReader dataReader) | ||
:base(dataReader) | ||
{ | ||
SqlDataReader = DataReader as SqlDataReader; | ||
} | ||
|
||
/// <summary> | ||
/// Asynchronously gets the data value as a type. | ||
/// </summary> | ||
/// <typeparam name="T">Type of value</typeparam> | ||
/// <param name="ordinal">Ordinal position of value</param> | ||
/// <returns></returns> | ||
public Task<T> GetFieldValueAsync<T>(int ordinal) | ||
{ | ||
if (SqlDataReader == null) | ||
throw new NotSupportedException("GetFieldValueAsync"); | ||
return SqlDataReader.GetFieldValueAsync<T>(ordinal); | ||
} | ||
|
||
/// <summary> | ||
/// Asynchronously gets the data value as a type. | ||
/// </summary> | ||
/// <typeparam name="T">Type of value</typeparam> | ||
/// <param name="ordinal">Ordinal position of value</param> | ||
/// <param name="cancellationToken">Async cancellation token</param> | ||
public Task<T> GetFieldValueAsync<T>(int ordinal, System.Threading.CancellationToken cancellationToken) | ||
{ | ||
if (SqlDataReader == null) | ||
throw new NotSupportedException("GetFieldValueAsync"); | ||
return SqlDataReader.GetFieldValueAsync<T>(ordinal, cancellationToken); | ||
} | ||
|
||
/// <summary> | ||
/// Gets a value indicating whether the column has a null | ||
/// or missing value. | ||
/// </summary> | ||
/// <param name="ordinal">Ordinal position of value</param> | ||
/// <returns></returns> | ||
public Task<bool> IsDbNullAsync(int ordinal) | ||
{ | ||
if (SqlDataReader == null) | ||
throw new NotSupportedException("IsDbNullAsync"); | ||
return SqlDataReader.IsDBNullAsync(ordinal); | ||
} | ||
|
||
/// <summary> | ||
/// Gets a value indicating whether the column has a null | ||
/// or missing value. | ||
/// </summary> | ||
/// <param name="ordinal">Ordinal position of value</param> | ||
/// <param name="cancellationToken">Async cancellation token</param> | ||
/// <returns></returns> | ||
public Task<bool> IsDbNullAsync(int ordinal, System.Threading.CancellationToken cancellationToken) | ||
{ | ||
if (SqlDataReader == null) | ||
throw new NotSupportedException("IsDbNullAsync"); | ||
return SqlDataReader.IsDBNullAsync(ordinal, cancellationToken); | ||
} | ||
|
||
/// <summary> | ||
/// Advances the reader to the next result. | ||
/// </summary> | ||
/// <returns></returns> | ||
public Task<bool> NextResultAsync() | ||
{ | ||
if (SqlDataReader == null) | ||
throw new NotSupportedException("NextResultAsync"); | ||
return SqlDataReader.NextResultAsync(); | ||
} | ||
|
||
/// <summary> | ||
/// Advances the reader to the next result. | ||
/// </summary> | ||
/// <param name="cancellationToken">Async cancellation token</param> | ||
/// <returns></returns> | ||
public Task<bool> NextResultAsync(System.Threading.CancellationToken cancellationToken) | ||
{ | ||
if (SqlDataReader == null) | ||
throw new NotSupportedException("NextResultAsync"); | ||
return SqlDataReader.NextResultAsync(cancellationToken); | ||
} | ||
|
||
/// <summary> | ||
/// Advances to the next record in a recordset. | ||
/// </summary> | ||
/// <returns></returns> | ||
public Task<bool> ReadAsync() | ||
{ | ||
if (SqlDataReader == null) | ||
throw new NotSupportedException("NextResultAsync"); | ||
return SqlDataReader.ReadAsync(); | ||
} | ||
|
||
/// <summary> | ||
/// Advances to the next record in a recordset. | ||
/// </summary> | ||
/// <param name="cancellationToken">Async cancellation token</param> | ||
/// <returns></returns> | ||
public Task<bool> ReadAsync(System.Threading.CancellationToken cancellationToken) | ||
{ | ||
if (SqlDataReader == null) | ||
throw new NotSupportedException("NextResultAsync"); | ||
return SqlDataReader.ReadAsync(cancellationToken); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.