Skip to content

Commit

Permalink
MarimerLLC#981 Move NS2 ConnectionManager to new project/namespace; s…
Browse files Browse the repository at this point in the history
…plit out SafeSqlDataReader functionality for NS2, leaving old implementation for .NET FX
  • Loading branch information
rockfordlhotka committed May 15, 2019
1 parent 000d897 commit 8ab18c7
Show file tree
Hide file tree
Showing 7 changed files with 480 additions and 92 deletions.
187 changes: 187 additions & 0 deletions Source/Csla.Data.SqlClient.Fx/ConnectionManager.cs
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 Source/Csla.Data.SqlClient.Fx/Csla.Data.SqlClient.Fx.csproj
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>
137 changes: 137 additions & 0 deletions Source/Csla.Data.SqlClient.Fx/SafeSqlDataReader.cs
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);
}
}
}
1 change: 0 additions & 1 deletion Source/Csla.NetStandard2.0/Csla.NetStandard2.0.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="2.1.1" />
<PackageReference Include="System.Data.Common" Version="4.3.0" />
<PackageReference Include="System.Data.SqlClient" Version="4.6.1" />
<PackageReference Include="System.Runtime.Serialization.Primitives" Version="4.3.0" />
<PackageReference Include="System.Security.Principal" Version="4.3.0" />
<PackageReference Include="System.Runtime.Serialization.Xml" Version="4.3.0" />
Expand Down
2 changes: 2 additions & 0 deletions Source/Csla.Shared/Data/ConnectionManager.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#if !NETSTANDARD2_0
//-----------------------------------------------------------------------
// <copyright file="ConnectionManager.cs" company="Marimer LLC">
// Copyright (c) Marimer LLC. All rights reserved.
Expand Down Expand Up @@ -210,3 +211,4 @@ public void Dispose()
}
}
}
#endif
Loading

0 comments on commit 8ab18c7

Please sign in to comment.