Unified · Split
| @@ -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 same | 188 | /// 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 same | 206 | /// the best way to make sure <paramref name="value"/> can be converted back to its original type is to use the same |
| @@ -21,6 +21,7 @@ · 20 unmodified lines | |||
| 21 | // | 21 | // |
| 22 | //****************************************************************************************************** | 22 | //****************************************************************************************************** |
| 23 | 23 | ||
| 24 | using System; | ||
| 24 | using System.ComponentModel.DataAnnotations; | 25 | using System.ComponentModel.DataAnnotations; |
| 25 | 26 | ||
| 26 | namespace GSF.ComponentModel.DataAnnotations | 27 | namespace 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., expressions | 30 | /// 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 : RegularExpressionAttribute | 33 | [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. |
| @@ -21,6 +21,7 @@ · 20 unmodified lines | |||
| 21 | // | 21 | // |
| 22 | //****************************************************************************************************** | 22 | //****************************************************************************************************** |
| 23 | 23 | ||
| 24 | using System; | ||
| 24 | using System.ComponentModel.DataAnnotations; | 25 | using System.ComponentModel.DataAnnotations; |
| 25 | 26 | ||
| 26 | namespace GSF.ComponentModel.DataAnnotations | 27 | namespace 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 : RegularExpressionAttribute | 32 | [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. |
| @@ -21,11 +21,14 @@ · 20 unmodified lines | |||
| 21 | // | 21 | // |
| 22 | //****************************************************************************************************** | 22 | //****************************************************************************************************** |
| 23 | 23 | ||
| 24 | using System; | ||
| 25 | |||
| 24 | namespace GSF.ComponentModel | 26 | namespace 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 : ValueExpressionAttributeBase | 32 | public sealed class DefaultValueExpressionAttribute : ValueExpressionAttributeBase |
| 30 | { | 33 | { |
| 31 | /// <summary> | 34 | /// <summary> |
| @@ -21,11 +21,14 @@ · 20 unmodified lines | |||
| 21 | // | 21 | // |
| 22 | //****************************************************************************************************** | 22 | //****************************************************************************************************** |
| 23 | 23 | ||
| 24 | using System; | ||
| 25 | |||
| 24 | namespace GSF.ComponentModel | 26 | namespace 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 : ValueExpressionAttributeBase | 32 | public sealed class UpdateValueExpressionAttribute : ValueExpressionAttributeBase |
| 30 | { | 33 | { |
| 31 | /// <summary> | 34 | /// <summary> |
| @@ -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 : ValueExpressionAttributeBase | 229 | 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 : ValueExpressionAttributeBase | 283 | 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; |
| @@ -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 restriction | 233 | /// 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 | } |
| @@ -21,17 +21,19 @@ · 20 unmodified lines | |||
| 21 | // | 21 | // |
| 22 | //****************************************************************************************************** | 22 | //****************************************************************************************************** |
| 23 | 23 | ||
| 24 | using System; | ||
| 24 | using System.Data; | 25 | using System.Data; |
| 25 | using System.Diagnostics.CodeAnalysis; | 26 | using System.Diagnostics.CodeAnalysis; |
| 26 | using System.Linq; | 27 | using System.Linq; |
| 27 | using System.Runtime.CompilerServices; | 28 | using System.Runtime.CompilerServices; |
| 29 | using GSF.Collections; | ||
| 28 | 30 | ||
| 29 | namespace GSF.Data.Model | 31 | namespace 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 RecordRestriction | 36 | 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 | #endregion | 65 | #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 filter | 70 | /// 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 | #endregion | 135 | #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, then | 185 | /// <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); |
| @@ -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 restriction | 618 | /// 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 model | 1517 | // 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() |
| @@ -23,6 +23,7 @@ · 22 unmodified lines | |||
| 23 | 23 | ||
| 24 | using System; | 24 | using System; |
| 25 | using System.ComponentModel; | 25 | using System.ComponentModel; |
| 26 | using System.Diagnostics.CodeAnalysis; | ||
| 26 | using System.IO; | 27 | using System.IO; |
| 27 | using System.Security; | 28 | using 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) |
| @@ -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" /> |
| @@ -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 | } | ||
| @@ -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 | } | ||
| @@ -40,7 +40,7 @@ namespace GSF.Web.Hubs · 39 unmodified lines | |||
| 40 | #region [ Members ] | 40 | #region [ Members ] |
| 41 | 41 | ||
| 42 | // Fields | 42 | // 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 | try | 137 | try |
| 138 | { | 138 | { |
| 139 | if (disposing) | 139 | if (disposing) |
| 140 | m_dataContext?.Dispose(); | 140 | { |
| 141 | m_clientScript = null; | ||
| 142 | } | ||
| 141 | } | 143 | } |
| 142 | finally | 144 | 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 | } |
| @@ -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 defaults | 74 | // 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 | ||