//******************************************************************************************************
// RecordOperationsHub.cs - Gbtc
//
// Copyright © 2016, Grid Protection Alliance. All Rights Reserved.
//
// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
// the NOTICE file distributed with this work for additional information regarding copyright ownership.
// The GPA licenses this file to you under the MIT License (MIT), the "License"; you may not use this
// file except in compliance with the License. You may obtain a copy of the License at:
//
// http://opensource.org/licenses/MIT
//
// Unless agreed to in writing, the subject software distributed under the License is distributed on an
// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
// License for the specific language governing permissions and limitations.
//
// Code Modification History:
// ----------------------------------------------------------------------------------------------------
// 08/17/2016 - J. Ritchie Carroll
// Generated original version of source code.
//
//******************************************************************************************************
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Configuration;
using Microsoft.AspNet.SignalR.Hubs;
using GSF.Data.Model;
using GSF.Web.Model;
// ReSharper disable StaticMemberInGenericType
namespace GSF.Web.Hubs
{
///
/// Defines a base class for implementing SignalR hubs that need record operations support.
///
/// Type of , typically inheritor.
public abstract class RecordOperationsHub : Hub, IRecordOperationsHub where T : class, IHub, IRecordOperationsHub
{
#region [ Members ]
// Fields
private readonly DataContextOperations m_dataContextOperations;
private readonly Action m_logStatusMessageFunction;
private readonly Action m_logExceptionFunction;
private DataContext m_dataContext;
private dynamic m_clientScript;
private string m_connectionID;
private bool m_disposed;
#endregion
#region [ Constructors ]
///
/// Creates a new .
///
protected RecordOperationsHub() :
this(null, null, null)
{
}
///
/// Creates a new with the specified logging functions.
///
/// Delegate to use to log status messages, if any.
/// Delegate to use to log exceptions, if any.
protected RecordOperationsHub(Action logStatusMessageFunction, Action logExceptionFunction) :
this(null, logStatusMessageFunction, logExceptionFunction)
{
}
///
/// Creates a new with the specified and logging functions.
///
/// Setting category that contains the connection settings.
/// Delegate to use to log status messages, if any.
/// Delegate to use to log exceptions, if any.
protected RecordOperationsHub(string settingsCategory, Action logStatusMessageFunction, Action logExceptionFunction)
{
m_dataContextOperations = new DataContextOperations(this, settingsCategory, logStatusMessageFunction, logExceptionFunction);
m_logStatusMessageFunction = logStatusMessageFunction;
m_logExceptionFunction = logExceptionFunction;
}
#endregion
#region [ Properties ]
///
/// Gets for SignalR hub.
///
public RecordOperationsCache RecordOperationsCache => s_recordOperationsCache;
///
/// Gets created for this instance.
///
///
/// Primary key caches for data context table operations are cached per user session.
///
public DataContext DataContext => m_dataContext ?? (m_dataContext = m_dataContextOperations.DataContext);
///
/// Gets reference to SignalR hub client browser DOM functionality.
///
///
/// This property can be used to call registered Javascript hub functions.
///
public dynamic ClientScript => m_clientScript ?? (m_clientScript = Clients?.Client(ConnectionID));
///
/// Gets active connection ID from current hub context or assigns one to use.
///
public string ConnectionID
{
get
{
if (string.IsNullOrEmpty(m_connectionID))
m_connectionID = Context?.ConnectionId;
return m_connectionID;
}
set
{
m_connectionID = value;
}
}
#endregion
#region [ Methods ]
///
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
///
/// true to release both managed and unmanaged resources; false to release only unmanaged resources.
protected override void Dispose(bool disposing)
{
if (!m_disposed)
{
try
{
if (disposing)
{
// Update any primary key caches for session
if ((object)m_dataContext != null && (object)m_dataContextOperations != null)
{
foreach (KeyValuePair item in m_dataContext.TableOperationsCache)
m_dataContextOperations.PrimaryKeySessionCache[item.Key] = item.Value.PrimaryKeyCache;
}
m_dataContext?.Dispose();
}
}
finally
{
m_disposed = true; // Prevent duplicate dispose.
base.Dispose(disposing); // Call base class Dispose().
}
}
}
///
/// Called when the connection connects to this hub instance.
///
/// A
public override Task OnConnected()
{
s_connectCount++;
return base.OnConnected();
}
///
/// Called when a connection disconnects from this hub gracefully or due to a timeout.
///
///
/// true if stop was called on the client closing the connection gracefully;
/// otherwise, false, if the connection has been lost for longer than the
/// . Timeouts can be caused
/// by clients reconnecting to another SignalR server in scale-out.
///
/// A
public override Task OnDisconnected(bool stopCalled)
{
if (stopCalled)
{
m_dataContextOperations?.EndSession();
s_connectCount--;
}
return base.OnDisconnected(stopCalled);
}
///
/// Logs a status message to hub client and any provided log status message delegate.
///
/// Message content to log.
/// Type of message to log.
/// Determines if message should be logged to hub client.
///
/// When is true, function will
/// call "sendInfoMessage" Javascript hub client function if defined.
///
protected void LogStatusMessage(string message, UpdateType type = UpdateType.Information, bool logToClient = true)
{
// Send status message to hub client
if (logToClient)
ClientScript?.sendInfoMessage(message, type == UpdateType.Information ? 2000 : -1);
// Send status message to program host
m_logStatusMessageFunction?.Invoke(message, type);
}
///
/// Logs an exception to hub client and any provided log exception delegate.
///
/// Exception to log.
/// Determines if message should be logged to hub client.
///
/// When is true, function will
/// call "sendErrorMessage" Javascript hub client function if defined.
///
protected void LogException(Exception ex, bool logToClient = true)
{
// Send exception to hub client
if (logToClient)
ClientScript?.sendErrorMessage(ex.Message, -1);
// Send exception to program host
m_logExceptionFunction?.Invoke(ex);
}
///
/// Gets connection ID for active hub context, if any.
///
/// Connection ID for active hub context.
///
/// This defines a client-side script function to return connection ID for active SignalR hub context.
///
public string GetConnectionID()
{
return ConnectionID;
}
#endregion
#region [ Static ]
// Static Fields
private static readonly RecordOperationsCache s_recordOperationsCache;
private static volatile int s_connectCount;
// Static Constructor
static RecordOperationsHub()
{
s_recordOperationsCache = new RecordOperationsCache();
}
// Static Properties
///
/// Gets the hub connection count.
///
public static int ConnectionCount => s_connectCount;
// Static Methods
///
/// Gets statically cached instance of for instances.
///
/// Statically cached instance of for instances.
public static RecordOperationsCache GetRecordOperationsCache() => s_recordOperationsCache;
#endregion
}
}