280 lines · 11.2 KB · C#
RawDownload
//******************************************************************************************************//  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 StaticMemberInGenericTypenamespace GSF.Web.Hubs{    /// <summary>    /// Defines a base class for implementing SignalR hubs that need record operations support.    /// </summary>    /// <typeparam name="T">Type of <see cref="IRecordOperationsHub"/>, typically inheritor.</typeparam>    public abstract class RecordOperationsHub<T> : Hub, IRecordOperationsHub where T : class, IHub, IRecordOperationsHub    {        #region [ Members ]         // Fields        private readonly DataContextOperations m_dataContextOperations;        private readonly Action<string, UpdateType> m_logStatusMessageFunction;        private readonly Action<Exception> m_logExceptionFunction;        private DataContext m_dataContext;        private dynamic m_clientScript;        private string m_connectionID;        private bool m_disposed;         #endregion         #region [ Constructors ]         /// <summary>        /// Creates a new <see cref="RecordOperationsHub{T}"/>.        /// </summary>        protected RecordOperationsHub() :            this(null, null, null)        {        }         /// <summary>        /// Creates a new <see cref="RecordOperationsHub{T}"/> with the specified logging functions.        /// </summary>        /// <param name="logStatusMessageFunction">Delegate to use to log status messages, if any.</param>        /// <param name="logExceptionFunction">Delegate to use to log exceptions, if any.</param>        protected RecordOperationsHub(Action<string, UpdateType> logStatusMessageFunction, Action<Exception> logExceptionFunction) :            this(null, logStatusMessageFunction, logExceptionFunction)        {        }         /// <summary>        /// Creates a new <see cref="RecordOperationsHub{T}"/> with the specified <see cref="DataContext"/> and logging functions.        /// </summary>        /// <param name="settingsCategory">Setting category that contains the connection settings.</param>        /// <param name="logStatusMessageFunction">Delegate to use to log status messages, if any.</param>        /// <param name="logExceptionFunction">Delegate to use to log exceptions, if any.</param>        protected RecordOperationsHub(string settingsCategory, Action<string, UpdateType> logStatusMessageFunction, Action<Exception> logExceptionFunction)        {            m_dataContextOperations = new DataContextOperations(this, settingsCategory, logStatusMessageFunction, logExceptionFunction);            m_logStatusMessageFunction = logStatusMessageFunction;            m_logExceptionFunction = logExceptionFunction;        }         #endregion         #region [ Properties ]         /// <summary>        /// Gets <see cref="IRecordOperationsHub.RecordOperationsCache"/> for SignalR hub.        /// </summary>        public RecordOperationsCache RecordOperationsCache => s_recordOperationsCache;         /// <summary>        /// Gets <see cref="Model.DataContext"/> created for this <see cref="RecordOperationsHub{T}"/> instance.        /// </summary>        /// <remarks>        /// Primary key caches for data context table operations are cached per user session.        /// </remarks>        public DataContext DataContext => m_dataContext ?? (m_dataContext = m_dataContextOperations.DataContext);         /// <summary>        /// Gets reference to SignalR hub client browser DOM functionality.        /// </summary>        /// <remarks>        /// This property can be used to call registered Javascript hub functions.        /// </remarks>        public dynamic ClientScript => m_clientScript ?? (m_clientScript = Clients?.Client(ConnectionID));         /// <summary>        /// Gets active connection ID from current hub context or assigns one to use.        /// </summary>        public string ConnectionID        {            get            {                if (string.IsNullOrEmpty(m_connectionID))                    m_connectionID = Context?.ConnectionId;                 return m_connectionID;            }            set            {                m_connectionID = value;            }        }         #endregion         #region [ Methods ]         /// <summary>        /// Releases the unmanaged resources used by the <see cref="RecordOperationsHub{T}"/> object and optionally releases the managed resources.        /// </summary>        /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>        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<Type, ITableOperations> 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().                }            }        }         /// <summary>        /// Called when the connection connects to this hub instance.        /// </summary>        /// <returns>A <see cref="Task" /></returns>        public override Task OnConnected()        {            s_connectCount++;             return base.OnConnected();        }         /// <summary>        /// Called when a connection disconnects from this hub gracefully or due to a timeout.        /// </summary>        /// <param name="stopCalled">        /// <c>true</c> if stop was called on the client closing the connection gracefully;        /// otherwise, <c>false</c>, if the connection has been lost for longer than the        /// <see cref="IConfigurationManager.DisconnectTimeout" />. Timeouts can be caused        /// by clients reconnecting to another SignalR server in scale-out.        /// </param>        /// <returns>A <see cref="Task" /></returns>        public override Task OnDisconnected(bool stopCalled)        {            if (stopCalled)            {                m_dataContextOperations?.EndSession();                s_connectCount--;            }             return base.OnDisconnected(stopCalled);        }         /// <summary>        /// Logs a status message to hub client and any provided log status message delegate.        /// </summary>        /// <param name="message">Message content to log.</param>        /// <param name="type">Type of message to log.</param>        /// <param name="logToClient">Determines if message should be logged to hub client.</param>        /// <remarks>        /// When <paramref name="logToClient"/> is <c>true</c>, function will        /// call "sendInfoMessage" Javascript hub client function if defined.        /// </remarks>        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);         }         /// <summary>        /// Logs an exception to hub client and any provided log exception delegate.        /// </summary>        /// <param name="ex">Exception to log.</param>        /// <param name="logToClient">Determines if message should be logged to hub client.</param>        /// <remarks>        /// When <paramref name="logToClient"/> is <c>true</c>, function will        /// call "sendErrorMessage" Javascript hub client function if defined.        /// </remarks>        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);        }         /// <summary>        /// Gets connection ID for active hub context, if any.        /// </summary>        /// <returns>Connection ID for active hub context.</returns>        /// <remarks>        /// This defines a client-side script function to return connection ID for active SignalR hub context.        /// </remarks>        public string GetConnectionID()        {            return ConnectionID;        }         #endregion         #region [ Static ]         // Static Fields        private static readonly RecordOperationsCache<T> s_recordOperationsCache;        private static volatile int s_connectCount;         // Static Constructor        static RecordOperationsHub()        {            s_recordOperationsCache = new RecordOperationsCache<T>();        }         // Static Properties         /// <summary>        /// Gets the hub connection count.        /// </summary>        public static int ConnectionCount => s_connectCount;         // Static Methods         /// <summary>        /// Gets statically cached instance of <see cref="RecordOperationsCache"/> for <see cref="RecordOperationsHub{T}"/> instances.        /// </summary>        /// <returns>Statically cached instance of <see cref="RecordOperationsCache"/> for <see cref="RecordOperationsHub{T}"/> instances.</returns>        public static RecordOperationsCache GetRecordOperationsCache() => s_recordOperationsCache;         #endregion    }}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t