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
180 /// <summary>180 /// <summary>
181 /// Converts this string into the specified type.181 /// Converts this string into the specified type.
182 /// </summary>182 /// </summary>
183 /// <typeparam name="T"><see cref="Type"/> to convert string to.</typeparam>
184 /// <param name="value">Source string to convert to type.</param>183 /// <param name="value">Source string to convert to type.</param>
185 /// <param name="type"><see cref="Type"/> to convert string to.</param>184 /// <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>
187 /// <remarks>186 /// <remarks>
188 /// This function makes use of a <see cref="TypeConverter"/> to convert this <see cref="string"/> to the specified <paramref name="type"/>,187 /// This function makes use of a <see cref="TypeConverter"/> to convert this <see cref="string"/> to the specified <paramref name="type"/>,
189 /// the best way to make sure <paramref name="value"/> can be converted back to its original type is to use the same188 /// 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
198 /// <summary>197 /// <summary>
199 /// Converts this string into the specified type.198 /// Converts this string into the specified type.
200 /// </summary>199 /// </summary>
201 /// <typeparam name="T"><see cref="Type"/> to convert string to.</typeparam>
202 /// <param name="value">Source string to convert to type.</param>200 /// <param name="value">Source string to convert to type.</param>
203 /// <param name="type"><see cref="Type"/> to convert string to.</param>201 /// <param name="type"><see cref="Type"/> to convert string to.</param>
204 /// <param name="culture"><see cref="CultureInfo"/> to use for the conversion.</param>202 /// <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>
206 /// <remarks>204 /// <remarks>
207 /// This function makes use of a <see cref="TypeConverter"/> to convert this <see cref="string"/> to the specified <paramref name="type"/>,205 /// This function makes use of a <see cref="TypeConverter"/> to convert this <see cref="string"/> to the specified <paramref name="type"/>,
208 /// the best way to make sure <paramref name="value"/> can be converted back to its original type is to use the same206 /// 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
21//21//
22//******************************************************************************************************22//******************************************************************************************************
23 23
24using System;
24using System.ComponentModel.DataAnnotations;25using System.ComponentModel.DataAnnotations;
25 26
26namespace GSF.ComponentModel.DataAnnotations27namespace GSF.ComponentModel.DataAnnotations
@@ -29,7 +30,8 @@ namespace GSF.ComponentModel.DataAnnotations · 2 unmodified lines
29 /// Represents a <see cref="RegularExpressionAttribute"/> for validating Acronyms, i.e., expressions30 /// Represents a <see cref="RegularExpressionAttribute"/> for validating Acronyms, i.e., expressions
30 /// that consist only of upper case letters, numbers, '!', '-', '@', '#', '_' , '.' or '$'.31 /// that consist only of upper case letters, numbers, '!', '-', '@', '#', '_' , '.' or '$'.
31 /// </summary>32 /// </summary>
32 public class AcronymValidationAttribute : RegularExpressionAttribute33 [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)]
34 public sealed class AcronymValidationAttribute : RegularExpressionAttribute
33 {35 {
34 /// <summary>36 /// <summary>
35 /// Defines the regular expression pattern used to validate value. 37 /// 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
21//21//
22//******************************************************************************************************22//******************************************************************************************************
23 23
24using System;
24using System.ComponentModel.DataAnnotations;25using System.ComponentModel.DataAnnotations;
25 26
26namespace GSF.ComponentModel.DataAnnotations27namespace GSF.ComponentModel.DataAnnotations
@@ -28,7 +29,8 @@ namespace GSF.ComponentModel.DataAnnotations · 1 unmodified line
28 /// <summary>29 /// <summary>
29 /// Represents a <see cref="RegularExpressionAttribute"/> for validating URL's.30 /// Represents a <see cref="RegularExpressionAttribute"/> for validating URL's.
30 /// </summary>31 /// </summary>
31 public class UrlValidationAttribute : RegularExpressionAttribute32 [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)]
33 public sealed class UrlValidationAttribute : RegularExpressionAttribute
32 {34 {
33 /// <summary>35 /// <summary>
34 /// Defines the regular expression pattern used to validate value. 36 /// 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
21//21//
22//******************************************************************************************************22//******************************************************************************************************
23 23
24using System;
25
24namespace GSF.ComponentModel26namespace GSF.ComponentModel
25{27{
26 /// <summary>28 /// <summary>
27 /// Defines a C# expression attribute that when evaluated will specify the default value for a property.29 /// Defines a C# expression attribute that when evaluated will specify the default value for a property.
28 /// </summary>30 /// </summary>
31 [AttributeUsage(AttributeTargets.All)]
29 public sealed class DefaultValueExpressionAttribute : ValueExpressionAttributeBase32 public sealed class DefaultValueExpressionAttribute : ValueExpressionAttributeBase
30 {33 {
31 /// <summary>34 /// <summary>
Source/Libraries/GSF.Core/ComponentModel/UpdateValueExpressionAttribute.cs+3−0
@@ -21,11 +21,14 @@ · 20 unmodified lines
21//21//
22//******************************************************************************************************22//******************************************************************************************************
23 23
24using System;
25
24namespace GSF.ComponentModel26namespace GSF.ComponentModel
25{27{
26 /// <summary>28 /// <summary>
27 /// Defines a C# expression attribute that when evaluated will specify an updated value for a property.29 /// Defines a C# expression attribute that when evaluated will specify an updated value for a property.
28 /// </summary>30 /// </summary>
31 [AttributeUsage(AttributeTargets.All)]
29 public sealed class UpdateValueExpressionAttribute : ValueExpressionAttributeBase32 public sealed class UpdateValueExpressionAttribute : ValueExpressionAttributeBase
30 {33 {
31 /// <summary>34 /// <summary>
Source/Libraries/GSF.Core/ComponentModel/ValueExpressionParser.cs+6−0
@@ -225,6 +225,7 @@ namespace GSF.ComponentModel · 224 unmodified lines
225 /// <returns>225 /// <returns>
226 /// Generated delegate that will create new <typeparamref name="T"/> instances with default values applied.226 /// Generated delegate that will create new <typeparamref name="T"/> instances with default values applied.
227 /// </returns>227 /// </returns>
228 [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
228 public static Func<T> CreateInstanceForType<TValueExpressionAttribute>(IEnumerable<PropertyInfo> properties = null, TypeRegistry typeRegistry = null) where TValueExpressionAttribute : ValueExpressionAttributeBase229 public static Func<T> CreateInstanceForType<TValueExpressionAttribute>(IEnumerable<PropertyInfo> properties = null, TypeRegistry typeRegistry = null) where TValueExpressionAttribute : ValueExpressionAttributeBase
229 {230 {
230 return () => CreateInstanceForType<TValueExpressionAttribute, MinimumScope>(properties, typeRegistry)(new MinimumScope());231 return () => CreateInstanceForType<TValueExpressionAttribute, MinimumScope>(properties, typeRegistry)(new MinimumScope());
@@ -278,6 +279,7 @@ namespace GSF.ComponentModel · 47 unmodified lines
278 /// Generated delegate that will update <typeparamref name="T"/> instances with update expression values applied.279 /// Generated delegate that will update <typeparamref name="T"/> instances with update expression values applied.
279 /// </returns>280 /// </returns>
280 /// <typeparam name="TValueExpressionAttribute"><see cref="ValueExpressionAttributeBase"/> parameter type.</typeparam>281 /// <typeparam name="TValueExpressionAttribute"><see cref="ValueExpressionAttributeBase"/> parameter type.</typeparam>
282 [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
281 public static Action<T> UpdateInstanceForType<TValueExpressionAttribute>(IEnumerable<PropertyInfo> properties = null, TypeRegistry typeRegistry = null) where TValueExpressionAttribute : ValueExpressionAttributeBase283 public static Action<T> UpdateInstanceForType<TValueExpressionAttribute>(IEnumerable<PropertyInfo> properties = null, TypeRegistry typeRegistry = null) where TValueExpressionAttribute : ValueExpressionAttributeBase
282 {284 {
283 return instance => UpdateInstanceForType<TValueExpressionAttribute, MinimumScope>(properties, typeRegistry)(new MinimumScope { Instance = instance });285 return instance => UpdateInstanceForType<TValueExpressionAttribute, MinimumScope>(properties, typeRegistry)(new MinimumScope { Instance = instance });
@@ -345,6 +347,7 @@ namespace GSF.ComponentModel · 61 unmodified lines
345 /// </returns>347 /// </returns>
346 /// <typeparam name="TValueExpressionAttribute"><see cref="ValueExpressionAttributeBase"/> parameter type.</typeparam>348 /// <typeparam name="TValueExpressionAttribute"><see cref="ValueExpressionAttributeBase"/> parameter type.</typeparam>
347 /// <typeparam name="TExpressionScope"><see cref="ValueExpressionScopeBase{T}"/> parameter type.</typeparam>349 /// <typeparam name="TExpressionScope"><see cref="ValueExpressionScopeBase{T}"/> parameter type.</typeparam>
350 [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
348 [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]351 [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]
349 public static Func<TExpressionScope, T> CreateInstanceForType<TValueExpressionAttribute, TExpressionScope>(IEnumerable<PropertyInfo> properties = null, TypeRegistry typeRegistry = null) where TValueExpressionAttribute : ValueExpressionAttributeBase where TExpressionScope : ValueExpressionScopeBase<T>352 public static Func<TExpressionScope, T> CreateInstanceForType<TValueExpressionAttribute, TExpressionScope>(IEnumerable<PropertyInfo> properties = null, TypeRegistry typeRegistry = null) where TValueExpressionAttribute : ValueExpressionAttributeBase where TExpressionScope : ValueExpressionScopeBase<T>
350 {353 {
@@ -473,6 +476,7 @@ namespace GSF.ComponentModel · 122 unmodified lines
473 /// </returns>476 /// </returns>
474 /// <typeparam name="TValueExpressionAttribute"><see cref="ValueExpressionAttributeBase"/> parameter type.</typeparam>477 /// <typeparam name="TValueExpressionAttribute"><see cref="ValueExpressionAttributeBase"/> parameter type.</typeparam>
475 /// <typeparam name="TExpressionScope"><see cref="ValueExpressionScopeBase{T}"/> parameter type.</typeparam>478 /// <typeparam name="TExpressionScope"><see cref="ValueExpressionScopeBase{T}"/> parameter type.</typeparam>
479 [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
476 [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]480 [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]
477 public static Action<TExpressionScope> UpdateInstanceForType<TValueExpressionAttribute, TExpressionScope>(IEnumerable<PropertyInfo> properties = null, TypeRegistry typeRegistry = null) where TValueExpressionAttribute : ValueExpressionAttributeBase where TExpressionScope : ValueExpressionScopeBase<T>481 public static Action<TExpressionScope> UpdateInstanceForType<TValueExpressionAttribute, TExpressionScope>(IEnumerable<PropertyInfo> properties = null, TypeRegistry typeRegistry = null) where TValueExpressionAttribute : ValueExpressionAttributeBase where TExpressionScope : ValueExpressionScopeBase<T>
478 {482 {
@@ -562,12 +566,14 @@ namespace GSF.ComponentModel · 83 unmodified lines
562 return Expression.Call(instance, property.SetMethod, getParsedValue);566 return Expression.Call(instance, property.SetMethod, getParsedValue);
563 }567 }
564 568
569 // Function referenced through reflection - see s_addCachedValueMethod
565 private static void AddCachedValue(PropertyInfo property, object value)570 private static void AddCachedValue(PropertyInfo property, object value)
566 {571 {
567 lock (s_cachedExpressionValues)572 lock (s_cachedExpressionValues)
568 s_cachedExpressionValues.GetOrAdd(property, value);573 s_cachedExpressionValues.GetOrAdd(property, value);
569 }574 }
570 575
576 // Function referenced through reflection - see s_getCachedValueMethod
571 private static Tuple<bool, object> GetCachedValue(PropertyInfo property)577 private static Tuple<bool, object> GetCachedValue(PropertyInfo property)
572 {578 {
573 bool exists;579 bool exists;
Source/Libraries/GSF.Core/Data/Model/ITableOperations.cs+28−2
@@ -225,7 +225,9 @@ namespace GSF.Data.Model · 224 unmodified lines
225 /// <param name="searchText">Text to search.</param>225 /// <param name="searchText">Text to search.</param>
226 /// <returns>An enumerable of modeled table row instances for queried records.</returns>226 /// <returns>An enumerable of modeled table row instances for queried records.</returns>
227 /// <para>227 /// <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.
229 /// </para>231 /// </para>
230 /// <para>232 /// <para>
231 /// This is a convenience call to <see cref="QueryRecords(string, bool, int, int, RecordRestriction)"/> where restriction233 /// 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
243 /// <param name="restriction">Record restriction to apply, if any.</param>245 /// <param name="restriction">Record restriction to apply, if any.</param>
244 /// <returns>An enumerable of modeled table row instances for queried records.</returns>246 /// <returns>An enumerable of modeled table row instances for queried records.</returns>
245 /// <remarks>247 /// <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.
247 /// </remarks>251 /// </remarks>
248 IEnumerable QueryRecords(string sortField, bool ascending, int page, int pageSize, RecordRestriction restriction = null);252 IEnumerable QueryRecords(string sortField, bool ascending, int page, int pageSize, RecordRestriction restriction = null);
249 253
@@ -552,5 +556,27 @@ namespace GSF.Data.Model · 302 unmodified lines
552 /// <see cref="SearchableAttribute"/> and specified <paramref name="searchText"/>.556 /// <see cref="SearchableAttribute"/> and specified <paramref name="searchText"/>.
553 /// </returns>557 /// </returns>
554 RecordRestriction GetSearchRestriction(string searchText);558 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();
555 }581 }
556}582}
Source/Libraries/GSF.Core/Data/Model/RecordRestriction.cs+72−12
@@ -21,17 +21,19 @@ · 20 unmodified lines
21//21//
22//******************************************************************************************************22//******************************************************************************************************
23 23
24using System;
24using System.Data;25using System.Data;
25using System.Diagnostics.CodeAnalysis;26using System.Diagnostics.CodeAnalysis;
26using System.Linq;27using System.Linq;
27using System.Runtime.CompilerServices;28using System.Runtime.CompilerServices;
29using GSF.Collections;
28 30
29namespace GSF.Data.Model31namespace GSF.Data.Model
30{32{
31 /// <summary>33 /// <summary>
32 /// Defines a parameterized record restriction that can be applied to queries.34 /// Defines a parameterized record restriction that can be applied to queries.
33 /// </summary>35 /// </summary>
34 public class RecordRestriction36 public class RecordRestriction : IEquatable<RecordRestriction>
35 {37 {
36 #region [ Members ]38 #region [ Members ]
37 39
@@ -53,25 +55,17 @@ namespace GSF.Data.Model · 15 unmodified lines
53 /// will be updated to reflect what is defined in the user model.55 /// will be updated to reflect what is defined in the user model.
54 /// </para>56 /// </para>
55 /// </remarks>57 /// </remarks>
56 public string FilterExpression;58 public readonly string FilterExpression;
57 59
58 /// <summary>60 /// <summary>
59 /// Defines restriction parameter values.61 /// Defines restriction parameter values.
60 /// </summary>62 /// </summary>
61 public object[] Parameters;63 public readonly object[] Parameters;
62 64
63 #endregion65 #endregion
64 66
65 #region [ Constructors ]67 #region [ Constructors ]
66 68
67 /// <summary>
68 /// Creates a new parameterized <see cref="RecordRestriction"/>.
69 /// </summary>
70 public RecordRestriction()
71 {
72 Parameters = new object[0];
73 }
74
75 /// <summary>69 /// <summary>
76 /// Creates a new parameterized <see cref="RecordRestriction"/> with the specified SQL filter70 /// Creates a new parameterized <see cref="RecordRestriction"/> with the specified SQL filter
77 /// expression and parameters.71 /// expression and parameters.
@@ -99,6 +93,45 @@ namespace GSF.Data.Model · 21 unmodified lines
99 Parameters = parameters;93 Parameters = parameters;
100 }94 }
101 95
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
102 #endregion135 #endregion
103 136
104 #region [ Operators ]137 #region [ Operators ]
@@ -110,6 +143,34 @@ namespace GSF.Data.Model · 5 unmodified lines
110 /// <returns>Record operation representing the specified filter expression.</returns>143 /// <returns>Record operation representing the specified filter expression.</returns>
111 public static implicit operator RecordRestriction(string value) => new RecordRestriction(value);144 public static implicit operator RecordRestriction(string value) => new RecordRestriction(value);
112 145
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
113 /// <summary>174 /// <summary>
114 /// Combines two record restrictions with an AND condition.175 /// Combines two record restrictions with an AND condition.
115 /// </summary>176 /// </summary>
@@ -124,7 +185,6 @@ namespace GSF.Data.Model · 8 unmodified lines
124 /// <c>null</c>, empty or whitespace and the other parameter's filter expression is defined, then185 /// <c>null</c>, empty or whitespace and the other parameter's filter expression is defined, then
125 /// the parameter that has a filter expression will be returned.186 /// the parameter that has a filter expression will be returned.
126 /// </remarks>187 /// </remarks>
127 [SuppressMessage("Microsoft.Design", "CA1013:OverloadOperatorEqualsOnOverloadingAddAndSubtract")]
128 public static RecordRestriction operator +(RecordRestriction left, RecordRestriction right)188 public static RecordRestriction operator +(RecordRestriction left, RecordRestriction right)
129 {189 {
130 return CombineAnd(left, right);190 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
76 private Action<Exception> m_exceptionHandler;76 private Action<Exception> m_exceptionHandler;
77 private IEnumerable<DataRow> m_primaryKeyCache;77 private IEnumerable<DataRow> m_primaryKeyCache;
78 private string m_lastSortField;78 private string m_lastSortField;
79 private RecordRestriction m_lastRestriction;
79 private bool m_useCaseSensitiveFieldNames;80 private bool m_useCaseSensitiveFieldNames;
80 private readonly string m_countSql;81 private readonly string m_countSql;
81 private readonly string m_orderBySql;82 private readonly string m_orderBySql;
@@ -609,7 +610,9 @@ namespace GSF.Data.Model · 527 unmodified lines
609 /// <returns>An enumerable of modeled table row instances for queried records.</returns>610 /// <returns>An enumerable of modeled table row instances for queried records.</returns>
610 /// <remarks>611 /// <remarks>
611 /// <para>612 /// <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.
613 /// </para>616 /// </para>
614 /// <para>617 /// <para>
615 /// This is a convenience call to <see cref="QueryRecords(string, bool, int, int, RecordRestriction)"/> where restriction618 /// 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
636 /// <param name="restriction">Record restriction to apply, if any.</param>639 /// <param name="restriction">Record restriction to apply, if any.</param>
637 /// <returns>An enumerable of modeled table row instances for queried records.</returns>640 /// <returns>An enumerable of modeled table row instances for queried records.</returns>
638 /// <remarks>641 /// <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>
640 /// </remarks>647 /// </remarks>
641 public IEnumerable<T> QueryRecords(string sortField, bool ascending, int page, int pageSize, RecordRestriction restriction = null)648 public IEnumerable<T> QueryRecords(string sortField, bool ascending, int page, int pageSize, RecordRestriction restriction = null)
642 {649 {
643 if (string.IsNullOrWhiteSpace(sortField))650 if (string.IsNullOrWhiteSpace(sortField))
644 sortField = s_fieldNames[s_primaryKeyProperties[0].Name];651 sortField = s_fieldNames[s_primaryKeyProperties[0].Name];
645 652
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)
647 {654 {
648 string orderByExpression = $"{sortField}{(ascending ? "" : " DESC")}";655 string orderByExpression = $"{sortField}{(ascending ? "" : " DESC")}";
649 string sqlExpression = null;656 string sqlExpression = null;
@@ -660,7 +667,6 @@ namespace GSF.Data.Model · 10 unmodified lines
660 sqlExpression = string.Format(m_orderByWhereSql, UpdateFieldNames(restriction.FilterExpression), orderByExpression);667 sqlExpression = string.Format(m_orderByWhereSql, UpdateFieldNames(restriction.FilterExpression), orderByExpression);
661 m_primaryKeyCache = m_connection.RetrieveData(sqlExpression, restriction.Parameters).AsEnumerable();668 m_primaryKeyCache = m_connection.RetrieveData(sqlExpression, restriction.Parameters).AsEnumerable();
662 }669 }
663
664 }670 }
665 catch (Exception ex)671 catch (Exception ex)
666 {672 {
@@ -674,6 +680,7 @@ namespace GSF.Data.Model · 7 unmodified lines
674 }680 }
675 681
676 m_lastSortField = sortField;682 m_lastSortField = sortField;
683 m_lastRestriction = restriction;
677 }684 }
678 685
679 return m_primaryKeyCache.ToPagedList(page, pageSize).Select(row => LoadRecord(row.ItemArray)).Where(record => record != null);686 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
1479 return new RecordRestriction(multiKeyWordFilter.ToString(), keyWords.SelectMany(keyWord => new object[] { $"%{keyWord}%", keyWord }).ToArray());1486 return new RecordRestriction(multiKeyWordFilter.ToString(), keyWords.SelectMany(keyWord => new object[] { $"%{keyWord}%", keyWord }).ToArray());
1480 }1487 }
1481 1488
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
1482 // Derive table name, escaping it if requested by model1517 // Derive table name, escaping it if requested by model
1483 [MethodImpl(MethodImplOptions.AggressiveInlining)]1518 [MethodImpl(MethodImplOptions.AggressiveInlining)]
1484 private string GetEscapedTableName()1519 private string GetEscapedTableName()
Source/Libraries/GSF.Core/IO/SafeFileWatcher.cs+2−0
@@ -23,6 +23,7 @@ · 22 unmodified lines
23 23
24using System;24using System;
25using System.ComponentModel;25using System.ComponentModel;
26using System.Diagnostics.CodeAnalysis;
26using System.IO;27using System.IO;
27using System.Security;28using System.Security;
28 29
@@ -338,6 +339,7 @@ namespace GSF.IO · 309 unmodified lines
338 /// Releases the unmanaged resources used by the <see cref="SafeFileWatcher"/> object and optionally releases the managed resources.339 /// Releases the unmanaged resources used by the <see cref="SafeFileWatcher"/> object and optionally releases the managed resources.
339 /// </summary>340 /// </summary>
340 /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>341 /// <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")]
341 protected virtual void Dispose(bool disposing)343 protected virtual void Dispose(bool disposing)
342 {344 {
343 if (m_disposed)345 if (m_disposed)
Source/Libraries/GSF.Web/GSF.Web.csproj+2−0
@@ -156,6 +156,8 @@ · 155 unmodified lines
156 <Compile Include="Embedded\ISecurityService.cs" />156 <Compile Include="Embedded\ISecurityService.cs" />
157 <Compile Include="Hosting\WebPageController.cs" />157 <Compile Include="Hosting\WebPageController.cs" />
158 <Compile Include="Hosting\WebServer.cs" />158 <Compile Include="Hosting\WebServer.cs" />
159 <Compile Include="Hubs\DataContextHubClient.cs" />
160 <Compile Include="Hubs\DataContextOperations.cs" />
159 <Compile Include="Hubs\HubClientBase.cs" />161 <Compile Include="Hubs\HubClientBase.cs" />
160 <Compile Include="Hubs\HubClientOperationsBase.cs" />162 <Compile Include="Hubs\HubClientOperationsBase.cs" />
161 <Compile Include="Hubs\IHubClient.cs" />163 <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
24using GSF.Web.Model;
25using Microsoft.AspNet.SignalR;
26
27namespace 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
24using System;
25using GSF.Web.Model;
26using Microsoft.AspNet.SignalR.Hubs;
27
28namespace 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
40 #region [ Members ]40 #region [ Members ]
41 41
42 // Fields42 // Fields
43 private readonly DataContext m_dataContext;43 private readonly DataContextOperations m_dataContextOperations;
44 private readonly Action<string, UpdateType> m_logStatusMessageFunction;44 private readonly Action<string, UpdateType> m_logStatusMessageFunction;
45 private readonly Action<Exception> m_logExceptionFunction;45 private readonly Action<Exception> m_logExceptionFunction;
46 private dynamic m_clientScript;46 private dynamic m_clientScript;
@@ -72,12 +72,12 @@ namespace GSF.Web.Hubs · 25 unmodified lines
72 /// <summary>72 /// <summary>
73 /// Creates a new <see cref="RecordOperationsHub{T}"/> with the specified <see cref="DataContext"/> and logging functions.73 /// Creates a new <see cref="RecordOperationsHub{T}"/> with the specified <see cref="DataContext"/> and logging functions.
74 /// </summary>74 /// </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>
76 /// <param name="logStatusMessageFunction">Delegate to use to log status messages, if any.</param>76 /// <param name="logStatusMessageFunction">Delegate to use to log status messages, if any.</param>
77 /// <param name="logExceptionFunction">Delegate to use to log exceptions, if any.</param>77 /// <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)
79 {79 {
80 m_dataContext = dataContext ?? new DataContext(exceptionHandler: ex => LogException(ex));80 m_dataContextOperations = new DataContextOperations(this, settingsCategory, logStatusMessageFunction, logExceptionFunction);
81 m_logStatusMessageFunction = logStatusMessageFunction;81 m_logStatusMessageFunction = logStatusMessageFunction;
82 m_logExceptionFunction = logExceptionFunction;82 m_logExceptionFunction = logExceptionFunction;
83 }83 }
@@ -100,9 +100,9 @@ namespace GSF.Web.Hubs · 16 unmodified lines
100 public dynamic ClientScript => m_clientScript ?? (m_clientScript = Clients?.Client(ConnectionID));100 public dynamic ClientScript => m_clientScript ?? (m_clientScript = Clients?.Client(ConnectionID));
101 101
102 /// <summary>102 /// <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.
104 /// </summary>104 /// </summary>
105 public DataContext DataContext => m_dataContext;105 public DataContext DataContext => m_dataContextOperations.DataContext;
106 106
107 /// <summary>107 /// <summary>
108 /// Gets active connection ID from current hub context or assigns one to use.108 /// Gets active connection ID from current hub context or assigns one to use.
@@ -137,7 +137,9 @@ namespace GSF.Web.Hubs · 28 unmodified lines
137 try137 try
138 {138 {
139 if (disposing)139 if (disposing)
140 m_dataContext?.Dispose();140 {
141 m_clientScript = null;
142 }
141 }143 }
142 finally144 finally
143 {145 {
@@ -171,7 +173,10 @@ namespace GSF.Web.Hubs · 27 unmodified lines
171 public override Task OnDisconnected(bool stopCalled)173 public override Task OnDisconnected(bool stopCalled)
172 {174 {
173 if (stopCalled)175 if (stopCalled)
176 {
177 m_dataContextOperations?.EndSession();
174 s_connectCount--;178 s_connectCount--;
179 }
175 180
176 return base.OnDisconnected(stopCalled);181 return base.OnDisconnected(stopCalled);
177 }182 }
Source/Libraries/GSF.Web/Security/SecurityHub.cs+5−5
@@ -65,11 +65,11 @@ namespace GSF.Web.Security · 64 unmodified lines
65 /// <summary>65 /// <summary>
66 /// Creates a new <see cref="SecurityHub"/> with the specified <see cref="DataContext"/> and logging functions.66 /// Creates a new <see cref="SecurityHub"/> with the specified <see cref="DataContext"/> and logging functions.
67 /// </summary>67 /// </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>
69 /// <param name="logStatusMessageFunction">Delegate to use to log status messages, if any.</param>69 /// <param name="logStatusMessageFunction">Delegate to use to log status messages, if any.</param>
70 /// <param name="logExceptionFunction">Delegate to use to log exceptions, if any.</param>70 /// <param name="logExceptionFunction">Delegate to use to log exceptions, if any.</param>
71 public SecurityHub(DataContext dataContext, Action<string, UpdateType> logStatusMessageFunction, Action<Exception> logExceptionFunction) : 71 public SecurityHub(string settingsCategory, Action<string, UpdateType> logStatusMessageFunction, Action<Exception> logExceptionFunction) :
72 this(dataContext, logStatusMessageFunction, logExceptionFunction, true)72 this(settingsCategory, logStatusMessageFunction, logExceptionFunction, true)
73 {73 {
74 // Capture initial defaults74 // Capture initial defaults
75 if ((object)logStatusMessageFunction != null && (object)s_logStatusMessageFunction == null)75 if ((object)logStatusMessageFunction != null && (object)s_logStatusMessageFunction == null)
@@ -79,8 +79,8 @@ namespace GSF.Web.Security · 3 unmodified lines
79 s_logExceptionFunction = logExceptionFunction;79 s_logExceptionFunction = logExceptionFunction;
80 }80 }
81 81
82 private SecurityHub(DataContext dataContext, Action<string, UpdateType> logStatusMessageFunction, Action<Exception> logExceptionFunction, bool overload) :82 private SecurityHub(string settingsCategory, Action<string, UpdateType> logStatusMessageFunction, Action<Exception> logExceptionFunction, bool overload) :
83 base(dataContext ?? new DataContext("securityProvider"), logStatusMessageFunction ?? s_logStatusMessageFunction, logExceptionFunction ?? s_logExceptionFunction)83 base(settingsCategory ?? "securityProvider", logStatusMessageFunction ?? s_logStatusMessageFunction, logExceptionFunction ?? s_logExceptionFunction)
84 {84 {
85 }85 }
86 86

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