History

GSF.Web: Updated DataContext table operations to be cached per session hub-client.

J. Ritchie Carroll committed 9 years ago

Commit c890f791 parent f28b03415 files · +306 −36Browse files

Unified · Split

15 files
  1. Source/Libraries/GSF.Core.Shared/StringExtensions.cs +2 −4
  2. Source/Libraries/GSF.Core/ComponentModel/DataAnnotations/AcronymValidationAttribute.cs +3 −1
  3. Source/Libraries/GSF.Core/ComponentModel/DataAnnotations/UrlValidationAttribute.cs +3 −1
  4. Source/Libraries/GSF.Core/ComponentModel/DefaultValueExpressionAttribute.cs +3 −0
  5. Source/Libraries/GSF.Core/ComponentModel/UpdateValueExpressionAttribute.cs +3 −0
  6. Source/Libraries/GSF.Core/ComponentModel/ValueExpressionParser.cs +6 −0
  7. Source/Libraries/GSF.Core/Data/Model/ITableOperations.cs +28 −2
  8. Source/Libraries/GSF.Core/Data/Model/RecordRestriction.cs +72 −12
  9. Source/Libraries/GSF.Core/Data/Model/TableOperations.cs +39 −4
  10. Source/Libraries/GSF.Core/IO/SafeFileWatcher.cs +2 −0
  11. Source/Libraries/GSF.Web/GSF.Web.csproj +2 −0
  12. Source/Libraries/GSF.Web/Hubs/DataContextHubClient.cs +72 −0
  13. Source/Libraries/GSF.Web/Hubs/DataContextOperations.cs +54 −0
  14. Source/Libraries/GSF.Web/Hubs/RecordOperationsHub.cs +12 −7
  15. Source/Libraries/GSF.Web/Security/SecurityHub.cs +5 −5
Source/Libraries/GSF.Core.Shared/StringExtensions.cs+2−4
@@ -180,10 +180,9 @@ namespace GSF · 179 unmodified lines
180180 /// <summary>
181181 /// Converts this string into the specified type.
182182 /// </summary>
183− /// <typeparam name="T"><see cref="Type"/> to convert string to.</typeparam>
184183 /// <param name="value">Source string to convert to type.</param>
185184 /// <param name="type"><see cref="Type"/> to convert string to.</param>
186− /// <returns><see cref="string"/> converted to specified <see cref="Type"/>; default value of specified type T if conversion fails.</returns>
185+ /// <returns><see cref="string"/> converted to specified <see cref="Type"/>; default value of specified type if conversion fails.</returns>
187186 /// <remarks>
188187 /// This function makes use of a <see cref="TypeConverter"/> to convert this <see cref="string"/> to the specified <paramref name="type"/>,
189188 /// the best way to make sure <paramref name="value"/> can be converted back to its original type is to use the same
@@ -198,11 +197,10 @@ namespace GSF · 8 unmodified lines
198197 /// <summary>
199198 /// Converts this string into the specified type.
200199 /// </summary>
201− /// <typeparam name="T"><see cref="Type"/> to convert string to.</typeparam>
202200 /// <param name="value">Source string to convert to type.</param>
203201 /// <param name="type"><see cref="Type"/> to convert string to.</param>
204202 /// <param name="culture"><see cref="CultureInfo"/> to use for the conversion.</param>
205− /// <returns><see cref="string"/> converted to specified <see cref="Type"/>; default value of specified type T if conversion fails.</returns>
203+ /// <returns><see cref="string"/> converted to specified <see cref="Type"/>; default value of specified type if conversion fails.</returns>
206204 /// <remarks>
207205 /// This function makes use of a <see cref="TypeConverter"/> to convert this <see cref="string"/> to the specified <paramref name="type"/>,
208206 /// the best way to make sure <paramref name="value"/> can be converted back to its original type is to use the same
Source/Libraries/GSF.Core/ComponentModel/DataAnnotations/AcronymValidationAttribute.cs+3−1
@@ -21,6 +21,7 @@ · 20 unmodified lines
2121 //
2222 //******************************************************************************************************
2323
24+using System;
2425 using System.ComponentModel.DataAnnotations;
2526
2627 namespace GSF.ComponentModel.DataAnnotations
@@ -29,7 +30,8 @@ namespace GSF.ComponentModel.DataAnnotations · 2 unmodified lines
2930 /// Represents a <see cref="RegularExpressionAttribute"/> for validating Acronyms, i.e., expressions
3031 /// that consist only of upper case letters, numbers, '!', '-', '@', '#', '_' , '.' or '$'.
3132 /// </summary>
32− public class AcronymValidationAttribute : RegularExpressionAttribute
33+ [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)]
34+ public sealed class AcronymValidationAttribute : RegularExpressionAttribute
3335 {
3436 /// <summary>
3537 /// Defines the regular expression pattern used to validate value.
Source/Libraries/GSF.Core/ComponentModel/DataAnnotations/UrlValidationAttribute.cs+3−1
@@ -21,6 +21,7 @@ · 20 unmodified lines
2121 //
2222 //******************************************************************************************************
2323
24+using System;
2425 using System.ComponentModel.DataAnnotations;
2526
2627 namespace GSF.ComponentModel.DataAnnotations
@@ -28,7 +29,8 @@ namespace GSF.ComponentModel.DataAnnotations · 1 unmodified line
2829 /// <summary>
2930 /// Represents a <see cref="RegularExpressionAttribute"/> for validating URL's.
3031 /// </summary>
31− public class UrlValidationAttribute : RegularExpressionAttribute
32+ [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)]
33+ public sealed class UrlValidationAttribute : RegularExpressionAttribute
3234 {
3335 /// <summary>
3436 /// Defines the regular expression pattern used to validate value.
Source/Libraries/GSF.Core/ComponentModel/DefaultValueExpressionAttribute.cs+3−0
@@ -21,11 +21,14 @@ · 20 unmodified lines
2121 //
2222 //******************************************************************************************************
2323
24+using System;
25+
2426 namespace GSF.ComponentModel
2527 {
2628 /// <summary>
2729 /// Defines a C# expression attribute that when evaluated will specify the default value for a property.
2830 /// </summary>
31+ [AttributeUsage(AttributeTargets.All)]
2932 public sealed class DefaultValueExpressionAttribute : ValueExpressionAttributeBase
3033 {
3134 /// <summary>
Source/Libraries/GSF.Core/ComponentModel/UpdateValueExpressionAttribute.cs+3−0
@@ -21,11 +21,14 @@ · 20 unmodified lines
2121 //
2222 //******************************************************************************************************
2323
24+using System;
25+
2426 namespace GSF.ComponentModel
2527 {
2628 /// <summary>
2729 /// Defines a C# expression attribute that when evaluated will specify an updated value for a property.
2830 /// </summary>
31+ [AttributeUsage(AttributeTargets.All)]
2932 public sealed class UpdateValueExpressionAttribute : ValueExpressionAttributeBase
3033 {
3134 /// <summary>
Source/Libraries/GSF.Core/ComponentModel/ValueExpressionParser.cs+6−0
@@ -225,6 +225,7 @@ namespace GSF.ComponentModel · 224 unmodified lines
225225 /// <returns>
226226 /// Generated delegate that will create new <typeparamref name="T"/> instances with default values applied.
227227 /// </returns>
228+ [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
228229 public static Func<T> CreateInstanceForType<TValueExpressionAttribute>(IEnumerable<PropertyInfo> properties = null, TypeRegistry typeRegistry = null) where TValueExpressionAttribute : ValueExpressionAttributeBase
229230 {
230231 return () => CreateInstanceForType<TValueExpressionAttribute, MinimumScope>(properties, typeRegistry)(new MinimumScope());
@@ -278,6 +279,7 @@ namespace GSF.ComponentModel · 47 unmodified lines
278279 /// Generated delegate that will update <typeparamref name="T"/> instances with update expression values applied.
279280 /// </returns>
280281 /// <typeparam name="TValueExpressionAttribute"><see cref="ValueExpressionAttributeBase"/> parameter type.</typeparam>
282+ [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
281283 public static Action<T> UpdateInstanceForType<TValueExpressionAttribute>(IEnumerable<PropertyInfo> properties = null, TypeRegistry typeRegistry = null) where TValueExpressionAttribute : ValueExpressionAttributeBase
282284 {
283285 return instance => UpdateInstanceForType<TValueExpressionAttribute, MinimumScope>(properties, typeRegistry)(new MinimumScope { Instance = instance });
@@ -345,6 +347,7 @@ namespace GSF.ComponentModel · 61 unmodified lines
345347 /// </returns>
346348 /// <typeparam name="TValueExpressionAttribute"><see cref="ValueExpressionAttributeBase"/> parameter type.</typeparam>
347349 /// <typeparam name="TExpressionScope"><see cref="ValueExpressionScopeBase{T}"/> parameter type.</typeparam>
350+ [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
348351 [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]
349352 public static Func<TExpressionScope, T> CreateInstanceForType<TValueExpressionAttribute, TExpressionScope>(IEnumerable<PropertyInfo> properties = null, TypeRegistry typeRegistry = null) where TValueExpressionAttribute : ValueExpressionAttributeBase where TExpressionScope : ValueExpressionScopeBase<T>
350353 {
@@ -473,6 +476,7 @@ namespace GSF.ComponentModel · 122 unmodified lines
473476 /// </returns>
474477 /// <typeparam name="TValueExpressionAttribute"><see cref="ValueExpressionAttributeBase"/> parameter type.</typeparam>
475478 /// <typeparam name="TExpressionScope"><see cref="ValueExpressionScopeBase{T}"/> parameter type.</typeparam>
479+ [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
476480 [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]
477481 public static Action<TExpressionScope> UpdateInstanceForType<TValueExpressionAttribute, TExpressionScope>(IEnumerable<PropertyInfo> properties = null, TypeRegistry typeRegistry = null) where TValueExpressionAttribute : ValueExpressionAttributeBase where TExpressionScope : ValueExpressionScopeBase<T>
478482 {
@@ -562,12 +566,14 @@ namespace GSF.ComponentModel · 83 unmodified lines
562566 return Expression.Call(instance, property.SetMethod, getParsedValue);
563567 }
564568
569+ // Function referenced through reflection - see s_addCachedValueMethod
565570 private static void AddCachedValue(PropertyInfo property, object value)
566571 {
567572 lock (s_cachedExpressionValues)
568573 s_cachedExpressionValues.GetOrAdd(property, value);
569574 }
570575
576+ // Function referenced through reflection - see s_getCachedValueMethod
571577 private static Tuple<bool, object> GetCachedValue(PropertyInfo property)
572578 {
573579 bool exists;
Source/Libraries/GSF.Core/Data/Model/ITableOperations.cs+28−2
@@ -225,7 +225,9 @@ namespace GSF.Data.Model · 224 unmodified lines
225225 /// <param name="searchText">Text to search.</param>
226226 /// <returns>An enumerable of modeled table row instances for queried records.</returns>
227227 /// <para>
228− /// This function is used for record paging. Primary keys are cached server-side, typically per user session, to maintain desired per-page sort order.
228+ /// This function is used for record paging. Primary keys are cached server-side, typically per user session,
229+ /// to maintain desired per-page sort order. Call <see cref="ClearPrimaryKeyCache"/> to manually clear cache
230+ /// when table contents are known to have changed.
229231 /// </para>
230232 /// <para>
231233 /// This is a convenience call to <see cref="QueryRecords(string, bool, int, int, RecordRestriction)"/> where restriction
@@ -243,7 +245,9 @@ namespace GSF.Data.Model · 11 unmodified lines
243245 /// <param name="restriction">Record restriction to apply, if any.</param>
244246 /// <returns>An enumerable of modeled table row instances for queried records.</returns>
245247 /// <remarks>
246− /// This function is used for record paging. Primary keys are cached server-side, typically per user session, to maintain desired per-page sort order.
248+ /// This function is used for record paging. Primary keys are cached server-side, typically per user session,
249+ /// to maintain desired per-page sort order. Call <see cref="ClearPrimaryKeyCache"/> to manually clear cache
250+ /// when table contents are known to have changed.
247251 /// </remarks>
248252 IEnumerable QueryRecords(string sortField, bool ascending, int page, int pageSize, RecordRestriction restriction = null);
249253
@@ -552,5 +556,27 @@ namespace GSF.Data.Model · 302 unmodified lines
552556 /// <see cref="SearchableAttribute"/> and specified <paramref name="searchText"/>.
553557 /// </returns>
554558 RecordRestriction GetSearchRestriction(string searchText);
559+
560+ /// <summary>
561+ /// Calculates the size of the current primary key cache.
562+ /// </summary>
563+ /// <returns></returns>
564+ int GetPrimaryKeyCacheSize();
565+
566+ /// <summary>
567+ /// Clears the primary key cache for this <see cref="ITableOperations"/> instance.
568+ /// </summary>
569+ /// <remarks>
570+ /// <para>
571+ /// This method is intended to be used in conjunction with calls to the overloads for
572+ /// <see cref="QueryRecords(string, bool, int, int, RecordRestriction)"/> which are
573+ /// used for record pagination.
574+ /// </para>
575+ /// <para>
576+ /// If record set is known to have changed outside purview of this class, this method
577+ /// should be called so that primary key cache can be reloaded.
578+ /// </para>
579+ /// </remarks>
580+ void ClearPrimaryKeyCache();
555581 }
556582 }
Source/Libraries/GSF.Core/Data/Model/RecordRestriction.cs+72−12
@@ -21,17 +21,19 @@ · 20 unmodified lines
2121 //
2222 //******************************************************************************************************
2323
24+using System;
2425 using System.Data;
2526 using System.Diagnostics.CodeAnalysis;
2627 using System.Linq;
2728 using System.Runtime.CompilerServices;
29+using GSF.Collections;
2830
2931 namespace GSF.Data.Model
3032 {
3133 /// <summary>
3234 /// Defines a parameterized record restriction that can be applied to queries.
3335 /// </summary>
34− public class RecordRestriction
36+ public class RecordRestriction : IEquatable<RecordRestriction>
3537 {
3638 #region [ Members ]
3739
@@ -53,25 +55,17 @@ namespace GSF.Data.Model · 15 unmodified lines
5355 /// will be updated to reflect what is defined in the user model.
5456 /// </para>
5557 /// </remarks>
56− public string FilterExpression;
58+ public readonly string FilterExpression;
5759
5860 /// <summary>
5961 /// Defines restriction parameter values.
6062 /// </summary>
61− public object[] Parameters;
63+ public readonly object[] Parameters;
6264
6365 #endregion
6466
6567 #region [ Constructors ]
6668
67− /// <summary>
68− /// Creates a new parameterized <see cref="RecordRestriction"/>.
69− /// </summary>
70− public RecordRestriction()
71− {
72− Parameters = new object[0];
73− }
74−
7569 /// <summary>
7670 /// Creates a new parameterized <see cref="RecordRestriction"/> with the specified SQL filter
7771 /// expression and parameters.
@@ -99,6 +93,45 @@ namespace GSF.Data.Model · 21 unmodified lines
9993 Parameters = parameters;
10094 }
10195
96+ /// <summary>
97+ /// Determines whether the specified object is equal to the current object.
98+ /// </summary>
99+ /// <param name="obj">An object to compare with this object.</param>
100+ /// <returns><c>true</c> if the current object is equal to the <paramref name="obj" /> parameter; otherwise, <c>false</c>.</returns>
101+ public override bool Equals(object obj)
102+ {
103+ RecordRestriction other = obj as RecordRestriction;
104+ return (object)other != null && Equals(other);
105+ }
106+
107+ /// <summary>
108+ /// Indicates whether the current object is equal to another object of the same type.
109+ /// </summary>
110+ /// <param name="other">An object to compare with this object.</param>
111+ /// <returns><c>true</c> if the current object is equal to the <paramref name="other" /> parameter; otherwise, <c>false</c>.</returns>
112+ public bool Equals(RecordRestriction other)
113+ {
114+ if (ReferenceEquals(this, other))
115+ return true;
116+
117+ if (FilterExpression.Equals(other?.FilterExpression, StringComparison.Ordinal))
118+ return Parameters.CompareTo(other?.Parameters, true) == 0;
119+
120+ return false;
121+ }
122+
123+ /// <summary>
124+ /// Serves as the default hash function.
125+ /// </summary>
126+ /// <returns>A hash code for the current <see cref="RecordRestriction"/>.</returns>
127+ public override int GetHashCode()
128+ {
129+ unchecked
130+ {
131+ return ((FilterExpression?.GetHashCode() ?? 0) * 397) ^ (Parameters?.GetHashCode() ?? 0);
132+ }
133+ }
134+
102135 #endregion
103136
104137 #region [ Operators ]
@@ -110,6 +143,34 @@ namespace GSF.Data.Model · 5 unmodified lines
110143 /// <returns>Record operation representing the specified filter expression.</returns>
111144 public static implicit operator RecordRestriction(string value) => new RecordRestriction(value);
112145
146+ /// <summary>
147+ /// Compares to record restrictions for equality.
148+ /// </summary>
149+ /// <param name="left"><see cref="RecordRestriction"/> left operand.</param>
150+ /// <param name="right"><see cref="RecordRestriction"/> right operand.</param>
151+ /// <returns><c>true</c> if record restrictions are equal; otherwise, <c>false</c>.</returns>
152+ public static bool operator ==(RecordRestriction left, RecordRestriction right)
153+ {
154+ if ((object)left == null && (object)right == null)
155+ return true;
156+
157+ return left?.Equals(right) ?? false;
158+ }
159+
160+ /// <summary>
161+ /// Compares to record restrictions for inequality.
162+ /// </summary>
163+ /// <param name="left"><see cref="RecordRestriction"/> left operand.</param>
164+ /// <param name="right"><see cref="RecordRestriction"/> right operand.</param>
165+ /// <returns><c>true</c> if record restrictions are not equal; otherwise, <c>false</c>.</returns>
166+ public static bool operator !=(RecordRestriction left, RecordRestriction right)
167+ {
168+ if ((object)left == null && (object)right == null)
169+ return false;
170+
171+ return !left?.Equals(right) ?? true;
172+ }
173+
113174 /// <summary>
114175 /// Combines two record restrictions with an AND condition.
115176 /// </summary>
@@ -124,7 +185,6 @@ namespace GSF.Data.Model · 8 unmodified lines
124185 /// <c>null</c>, empty or whitespace and the other parameter's filter expression is defined, then
125186 /// the parameter that has a filter expression will be returned.
126187 /// </remarks>
127− [SuppressMessage("Microsoft.Design", "CA1013:OverloadOperatorEqualsOnOverloadingAddAndSubtract")]
128188 public static RecordRestriction operator +(RecordRestriction left, RecordRestriction right)
129189 {
130190 return CombineAnd(left, right);
Source/Libraries/GSF.Core/Data/Model/TableOperations.cs+39−4
@@ -76,6 +76,7 @@ namespace GSF.Data.Model · 75 unmodified lines
7676 private Action<Exception> m_exceptionHandler;
7777 private IEnumerable<DataRow> m_primaryKeyCache;
7878 private string m_lastSortField;
79+ private RecordRestriction m_lastRestriction;
7980 private bool m_useCaseSensitiveFieldNames;
8081 private readonly string m_countSql;
8182 private readonly string m_orderBySql;
@@ -609,7 +610,9 @@ namespace GSF.Data.Model · 527 unmodified lines
609610 /// <returns>An enumerable of modeled table row instances for queried records.</returns>
610611 /// <remarks>
611612 /// <para>
612− /// This function is used for record paging. Primary keys are cached server-side, typically per user session, to maintain desired per-page sort order.
613+ /// This function is used for record paging. Primary keys are cached server-side, typically per user session,
614+ /// to maintain desired per-page sort order. Call <see cref="ClearPrimaryKeyCache"/> to manually clear cache
615+ /// when table contents are known to have changed.
613616 /// </para>
614617 /// <para>
615618 /// This is a convenience call to <see cref="QueryRecords(string, bool, int, int, RecordRestriction)"/> where restriction
@@ -636,14 +639,18 @@ namespace GSF.Data.Model · 20 unmodified lines
636639 /// <param name="restriction">Record restriction to apply, if any.</param>
637640 /// <returns>An enumerable of modeled table row instances for queried records.</returns>
638641 /// <remarks>
639− /// This function is used for record paging. Primary keys are cached server-side, typically per user session, to maintain desired per-page sort order.
642+ /// <para>
643+ /// This function is used for record paging. Primary keys are cached server-side, typically per user session,
644+ /// to maintain desired per-page sort order. Call <see cref="ClearPrimaryKeyCache"/> to manually clear cache
645+ /// when table contents are known to have changed.
646+ /// </para>
640647 /// </remarks>
641648 public IEnumerable<T> QueryRecords(string sortField, bool ascending, int page, int pageSize, RecordRestriction restriction = null)
642649 {
643650 if (string.IsNullOrWhiteSpace(sortField))
644651 sortField = s_fieldNames[s_primaryKeyProperties[0].Name];
645652
646− if ((object)m_primaryKeyCache == null || string.Compare(sortField, m_lastSortField, StringComparison.OrdinalIgnoreCase) != 0)
653+ if ((object)m_primaryKeyCache == null || !sortField.Equals(m_lastSortField, StringComparison.OrdinalIgnoreCase) || restriction != m_lastRestriction)
647654 {
648655 string orderByExpression = $"{sortField}{(ascending ? "" : " DESC")}";
649656 string sqlExpression = null;
@@ -660,7 +667,6 @@ namespace GSF.Data.Model · 10 unmodified lines
660667 sqlExpression = string.Format(m_orderByWhereSql, UpdateFieldNames(restriction.FilterExpression), orderByExpression);
661668 m_primaryKeyCache = m_connection.RetrieveData(sqlExpression, restriction.Parameters).AsEnumerable();
662669 }
663−
664670 }
665671 catch (Exception ex)
666672 {
@@ -674,6 +680,7 @@ namespace GSF.Data.Model · 7 unmodified lines
674680 }
675681
676682 m_lastSortField = sortField;
683+ m_lastRestriction = restriction;
677684 }
678685
679686 return m_primaryKeyCache.ToPagedList(page, pageSize).Select(row => LoadRecord(row.ItemArray)).Where(record => record != null);
@@ -1479,6 +1486,34 @@ namespace GSF.Data.Model · 799 unmodified lines
14791486 return new RecordRestriction(multiKeyWordFilter.ToString(), keyWords.SelectMany(keyWord => new object[] { $"%{keyWord}%", keyWord }).ToArray());
14801487 }
14811488
1489+ /// <summary>
1490+ /// Calculates the size of the current primary key cache.
1491+ /// </summary>
1492+ /// <returns></returns>
1493+ public int GetPrimaryKeyCacheSize()
1494+ {
1495+ return m_primaryKeyCache?.Count() ?? 0;
1496+ }
1497+
1498+ /// <summary>
1499+ /// Clears the primary key cache for this <see cref="TableOperations{T}"/> instance.
1500+ /// </summary>
1501+ /// <remarks>
1502+ /// <para>
1503+ /// This method is intended to be used in conjunction with calls to the overloads for
1504+ /// <see cref="QueryRecords(string, bool, int, int, RecordRestriction)"/> which are
1505+ /// used for record pagination.
1506+ /// </para>
1507+ /// <para>
1508+ /// If record set is known to have changed outside purview of this class, this method
1509+ /// should be called so that primary key cache can be reloaded.
1510+ /// </para>
1511+ /// </remarks>
1512+ public void ClearPrimaryKeyCache()
1513+ {
1514+ m_primaryKeyCache = null;
1515+ }
1516+
14821517 // Derive table name, escaping it if requested by model
14831518 [MethodImpl(MethodImplOptions.AggressiveInlining)]
14841519 private string GetEscapedTableName()
Source/Libraries/GSF.Core/IO/SafeFileWatcher.cs+2−0
@@ -23,6 +23,7 @@ · 22 unmodified lines
2323
2424 using System;
2525 using System.ComponentModel;
26+using System.Diagnostics.CodeAnalysis;
2627 using System.IO;
2728 using System.Security;
2829
@@ -338,6 +339,7 @@ namespace GSF.IO · 309 unmodified lines
338339 /// Releases the unmanaged resources used by the <see cref="SafeFileWatcher"/> object and optionally releases the managed resources.
339340 /// </summary>
340341 /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
342+ [SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed", MessageId = "m_fileSystemWatcher")]
341343 protected virtual void Dispose(bool disposing)
342344 {
343345 if (m_disposed)
Source/Libraries/GSF.Web/GSF.Web.csproj+2−0
@@ -156,6 +156,8 @@ · 155 unmodified lines
156156 <Compile Include="Embedded\ISecurityService.cs" />
157157 <Compile Include="Hosting\WebPageController.cs" />
158158 <Compile Include="Hosting\WebServer.cs" />
159+ <Compile Include="Hubs\DataContextHubClient.cs" />
160+ <Compile Include="Hubs\DataContextOperations.cs" />
159161 <Compile Include="Hubs\HubClientBase.cs" />
160162 <Compile Include="Hubs\HubClientOperationsBase.cs" />
161163 <Compile Include="Hubs\IHubClient.cs" />
Source/Libraries/GSF.Web/Hubs/DataContextHubClient.cs+72−0
@@ -0,0 +1,72 @@
1+//******************************************************************************************************
2+// DataContextHubClient.cs - Gbtc
3+//
4+// Copyright © 2017, Grid Protection Alliance. All Rights Reserved.
5+//
6+// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
7+// the NOTICE file distributed with this work for additional information regarding copyright ownership.
8+// The GPA licenses this file to you under the MIT License (MIT), the "License"; you may not use this
9+// file except in compliance with the License. You may obtain a copy of the License at:
10+//
11+// http://opensource.org/licenses/MIT
12+//
13+// Unless agreed to in writing, the subject software distributed under the License is distributed on an
14+// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
15+// License for the specific language governing permissions and limitations.
16+//
17+// Code Modification History:
18+// ----------------------------------------------------------------------------------------------------
19+// 04/26/2017 - J. Ritchie Carroll
20+// Generated original version of source code.
21+//
22+//******************************************************************************************************
23+
24+using GSF.Web.Model;
25+using Microsoft.AspNet.SignalR;
26+
27+namespace GSF.Web.Hubs
28+{
29+ /// <summary>
30+ /// Represents a SignalR <see cref="Hub"/> client session instance of a data context.
31+ /// </summary>
32+ public class DataContextHubClient : HubClientBase
33+ {
34+ #region [ Members ]
35+
36+ // Fields
37+ private DataContext m_dataContext;
38+ private bool m_disposed;
39+
40+ #endregion
41+
42+ #region [ Methods ]
43+
44+ /// <summary>
45+ /// Gets <see cref="Model.DataContext"/> instance for the current SignalR hub session, creating it if needed.
46+ /// </summary>
47+ public DataContext GetDataContext(string settingsCategory) => m_dataContext ?? (m_dataContext = new DataContext(settingsCategory ?? "systemSettings", exceptionHandler: ex => LogException(ex)));
48+
49+ /// <summary>
50+ /// Releases the unmanaged resources used by the <see cref="DataContextHubClient"/> object and optionally releases the managed resources.
51+ /// </summary>
52+ /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
53+ protected override void Dispose(bool disposing)
54+ {
55+ if (!m_disposed)
56+ {
57+ try
58+ {
59+ if (disposing)
60+ m_dataContext?.Dispose();
61+ }
62+ finally
63+ {
64+ m_disposed = true; // Prevent duplicate dispose.
65+ base.Dispose(disposing); // Call base class Dispose().
66+ }
67+ }
68+ }
69+
70+ #endregion
71+ }
72+}
Source/Libraries/GSF.Web/Hubs/DataContextOperations.cs+54−0
@@ -0,0 +1,54 @@
1+//******************************************************************************************************
2+// DataContextOperations.cs - Gbtc
3+//
4+// Copyright © 2017, Grid Protection Alliance. All Rights Reserved.
5+//
6+// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
7+// the NOTICE file distributed with this work for additional information regarding copyright ownership.
8+// The GPA licenses this file to you under the MIT License (MIT), the "License"; you may not use this
9+// file except in compliance with the License. You may obtain a copy of the License at:
10+//
11+// http://opensource.org/licenses/MIT
12+//
13+// Unless agreed to in writing, the subject software distributed under the License is distributed on an
14+// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
15+// License for the specific language governing permissions and limitations.
16+//
17+// Code Modification History:
18+// ----------------------------------------------------------------------------------------------------
19+// 04/26/2017 - J. Ritchie Carroll
20+// Generated original version of source code.
21+//
22+//******************************************************************************************************
23+
24+using System;
25+using GSF.Web.Model;
26+using Microsoft.AspNet.SignalR.Hubs;
27+
28+namespace GSF.Web.Hubs
29+{
30+ /// <summary>
31+ /// Represents hub operations for using <see cref="DataContextHubClient"/> instances.
32+ /// </summary>
33+ public class DataContextOperations : HubClientOperationsBase<DataContextHubClient>
34+ {
35+ private readonly string m_settingsCategory;
36+
37+ /// <summary>
38+ /// Creates a new <see cref="DataContextOperations"/> instance.
39+ /// </summary>
40+ /// <param name="hub">Parent hub.</param>
41+ /// <param name="settingsCategory">Setting category that contains the connection settings.</param>
42+ /// <param name="logStatusMessageFunction">Delegate to use to log status messages, if any.</param>
43+ /// <param name="logExceptionFunction">Delegate to use to log exceptions, if any.</param>
44+ public DataContextOperations(IHub hub, string settingsCategory, Action<string, UpdateType> logStatusMessageFunction = null, Action<Exception> logExceptionFunction = null) : base(hub, logStatusMessageFunction, logExceptionFunction)
45+ {
46+ m_settingsCategory = settingsCategory;
47+ }
48+
49+ /// <summary>
50+ /// Gets <see cref="Model.DataContext"/> instance for the current SignalR hub session, creating it if needed.
51+ /// </summary>
52+ public DataContext DataContext => HubClient.GetDataContext(m_settingsCategory);
53+ }
54+}
Source/Libraries/GSF.Web/Hubs/RecordOperationsHub.cs+12−7
@@ -40,7 +40,7 @@ namespace GSF.Web.Hubs · 39 unmodified lines
4040 #region [ Members ]
4141
4242 // Fields
43− private readonly DataContext m_dataContext;
43+ private readonly DataContextOperations m_dataContextOperations;
4444 private readonly Action<string, UpdateType> m_logStatusMessageFunction;
4545 private readonly Action<Exception> m_logExceptionFunction;
4646 private dynamic m_clientScript;
@@ -72,12 +72,12 @@ namespace GSF.Web.Hubs · 25 unmodified lines
7272 /// <summary>
7373 /// Creates a new <see cref="RecordOperationsHub{T}"/> with the specified <see cref="DataContext"/> and logging functions.
7474 /// </summary>
75− /// <param name="dataContext">Data context to use for this hub; set to <c>null</c> for default data context.</param>
75+ /// <param name="settingsCategory">Setting category that contains the connection settings.</param>
7676 /// <param name="logStatusMessageFunction">Delegate to use to log status messages, if any.</param>
7777 /// <param name="logExceptionFunction">Delegate to use to log exceptions, if any.</param>
78− protected RecordOperationsHub(DataContext dataContext, Action<string, UpdateType> logStatusMessageFunction, Action<Exception> logExceptionFunction)
78+ protected RecordOperationsHub(string settingsCategory, Action<string, UpdateType> logStatusMessageFunction, Action<Exception> logExceptionFunction)
7979 {
80− m_dataContext = dataContext ?? new DataContext(exceptionHandler: ex => LogException(ex));
80+ m_dataContextOperations = new DataContextOperations(this, settingsCategory, logStatusMessageFunction, logExceptionFunction);
8181 m_logStatusMessageFunction = logStatusMessageFunction;
8282 m_logExceptionFunction = logExceptionFunction;
8383 }
@@ -100,9 +100,9 @@ namespace GSF.Web.Hubs · 16 unmodified lines
100100 public dynamic ClientScript => m_clientScript ?? (m_clientScript = Clients?.Client(ConnectionID));
101101
102102 /// <summary>
103− /// Gets <see cref="Model.DataContext"/> created for this <see cref="RecordOperationsHub{T}"/> instance.
103+ /// Gets <see cref="Model.DataContext"/> instance for the current SignalR hub session, creating it if needed.
104104 /// </summary>
105− public DataContext DataContext => m_dataContext;
105+ public DataContext DataContext => m_dataContextOperations.DataContext;
106106
107107 /// <summary>
108108 /// Gets active connection ID from current hub context or assigns one to use.
@@ -137,7 +137,9 @@ namespace GSF.Web.Hubs · 28 unmodified lines
137137 try
138138 {
139139 if (disposing)
140− m_dataContext?.Dispose();
140+ {
141+ m_clientScript = null;
142+ }
141143 }
142144 finally
143145 {
@@ -171,7 +173,10 @@ namespace GSF.Web.Hubs · 27 unmodified lines
171173 public override Task OnDisconnected(bool stopCalled)
172174 {
173175 if (stopCalled)
176+ {
177+ m_dataContextOperations?.EndSession();
174178 s_connectCount--;
179+ }
175180
176181 return base.OnDisconnected(stopCalled);
177182 }
Source/Libraries/GSF.Web/Security/SecurityHub.cs+5−5
@@ -65,11 +65,11 @@ namespace GSF.Web.Security · 64 unmodified lines
6565 /// <summary>
6666 /// Creates a new <see cref="SecurityHub"/> with the specified <see cref="DataContext"/> and logging functions.
6767 /// </summary>
68− /// <param name="dataContext">Data context to use for this hub; set to <c>null</c> for default security provider context.</param>
68+ /// <param name="settingsCategory">Setting category that contains the connection settings. Defaults to "securityProvider".</param>
6969 /// <param name="logStatusMessageFunction">Delegate to use to log status messages, if any.</param>
7070 /// <param name="logExceptionFunction">Delegate to use to log exceptions, if any.</param>
71− public SecurityHub(DataContext dataContext, Action<string, UpdateType> logStatusMessageFunction, Action<Exception> logExceptionFunction) :
72− this(dataContext, logStatusMessageFunction, logExceptionFunction, true)
71+ public SecurityHub(string settingsCategory, Action<string, UpdateType> logStatusMessageFunction, Action<Exception> logExceptionFunction) :
72+ this(settingsCategory, logStatusMessageFunction, logExceptionFunction, true)
7373 {
7474 // Capture initial defaults
7575 if ((object)logStatusMessageFunction != null && (object)s_logStatusMessageFunction == null)
@@ -79,8 +79,8 @@ namespace GSF.Web.Security · 3 unmodified lines
7979 s_logExceptionFunction = logExceptionFunction;
8080 }
8181
82− private SecurityHub(DataContext dataContext, Action<string, UpdateType> logStatusMessageFunction, Action<Exception> logExceptionFunction, bool overload) :
83− base(dataContext ?? new DataContext("securityProvider"), logStatusMessageFunction ?? s_logStatusMessageFunction, logExceptionFunction ?? s_logExceptionFunction)
82+ private SecurityHub(string settingsCategory, Action<string, UpdateType> logStatusMessageFunction, Action<Exception> logExceptionFunction, bool overload) :
83+ base(settingsCategory ?? "securityProvider", logStatusMessageFunction ?? s_logStatusMessageFunction, logExceptionFunction ?? s_logExceptionFunction)
8484 {
8585 }
8686

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