//****************************************************************************************************** // 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 } }