//******************************************************************************************************// DataContext.cs - Gbtc//// Copyright © 2016, Grid Protection Alliance. All Rights Reserved.//// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See// the NOTICE file distributed with this work for additional information regarding copyright ownership.// The GPA licenses this file to you under the MIT License (MIT), the "License"; you may not use this// file except in compliance with the License. You may obtain a copy of the License at://// http://opensource.org/licenses/MIT//// Unless agreed to in writing, the subject software distributed under the License is distributed on an// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the// License for the specific language governing permissions and limitations.//// Code Modification History:// ----------------------------------------------------------------------------------------------------// 02/01/2016 - J. Ritchie Carroll// Generated original version of source code.////****************************************************************************************************** using System;using System.Collections.Concurrent;using System.Collections.Generic;using System.ComponentModel.DataAnnotations;using System.Data;using System.Linq;using System.Net.Http;using System.Text;using System.Web.Routing;using GSF.Collections;using GSF.ComponentModel.DataAnnotations;using GSF.Data;using GSF.Data.Model;using GSF.Reflection;using GSF.Security;using GSF.Web.Hubs;using RazorEngine.Templating; // ReSharper disable once StaticMemberInGenericTypenamespace GSF.Web.Model{ /// <summary> /// Defines a data context for Razor views. /// </summary> /// <remarks> /// This class is used by views to render HTML form input templates, paged view model configurations, /// provide access to modeled table operations and general database access. /// </remarks> public class DataContext : IDisposable { #region [ Members ] // Constants /// <summary> /// Defines the regular expression used to validate URLs. /// </summary> public const string UrlValidation = UrlValidationAttribute.ValidationPattern; // Fields private AdoDataConnection m_connection; private readonly IRazorEngine m_razorEngine; private readonly Dictionary<Type, ITableOperations> m_tableOperationsCache; private ConcurrentDictionary<Type, DataTable> m_primaryKeySessionCache; private readonly Action<Exception> m_exceptionHandler; private Dictionary<Type, IEnumerable<KeyValuePair<string, string>>> m_customTableOperationTokens; private readonly Dictionary<string, Tuple<string, string>> m_fieldValidationParameters; private readonly List<Tuple<string, string>> m_fieldValueInitializers; private readonly List<Tuple<string, string, string, bool>> m_readonlyHotLinkFields; private readonly List<string> m_definedDateFields; private readonly string m_settingsCategory; private readonly bool m_disposeConnection; private string m_initialFocusField; private string m_addDateFieldTemplate; private string m_addInputFieldTemplate; private string m_addTextAreaFieldTemplate; private string m_addSelectFieldTemplate; private string m_addCheckBoxFieldTemplate; private bool m_disposed; #endregion #region [ Constructors ] /// <summary> /// Creates a new <see cref="DataContext"/>. /// </summary> /// <param name="connection"><see cref="AdoDataConnection"/> to use; defaults to a new connection.</param> /// <param name="disposeConnection">Set to <c>true</c> to dispose the provided <paramref name="connection"/>.</param> /// <param name="razorEngine">Razor engine instance to use for data context; set to <c>null</c> to use default embedded resources instance.</param> /// <param name="exceptionHandler">Delegate to handle exceptions.</param> public DataContext(AdoDataConnection connection = null, bool disposeConnection = false, IRazorEngine razorEngine = null, Action<Exception> exceptionHandler = null) { m_connection = connection; m_razorEngine = razorEngine ?? RazorEngine<CSharpEmbeddedResource>.Default; m_exceptionHandler = exceptionHandler; m_tableOperationsCache = new Dictionary<Type, ITableOperations>(); m_fieldValidationParameters = new Dictionary<string, Tuple<string, string>>(); m_fieldValueInitializers = new List<Tuple<string, string>>(); m_readonlyHotLinkFields = new List<Tuple<string, string, string, bool>>(); m_definedDateFields = new List<string>(); m_settingsCategory = "systemSettings"; m_disposeConnection = disposeConnection || connection == null; } /// <summary> /// Creates a new <see cref="DataContext"/> using the specified <paramref name="settingsCategory"/>. /// </summary> /// <param name="settingsCategory">Setting category that contains the connection settings.</param> /// <param name="razorEngine">Razor engine instance to use for data context; set to <c>null</c> to use default embedded resources instance.</param> /// <param name="exceptionHandler">Delegate to handle exceptions.</param> public DataContext(string settingsCategory, IRazorEngine razorEngine = null, Action<Exception> exceptionHandler = null) { m_razorEngine = razorEngine ?? RazorEngine<CSharpEmbeddedResource>.Default; m_exceptionHandler = exceptionHandler; m_tableOperationsCache = new Dictionary<Type, ITableOperations>(); m_fieldValidationParameters = new Dictionary<string, Tuple<string, string>>(); m_fieldValueInitializers = new List<Tuple<string, string>>(); m_readonlyHotLinkFields = new List<Tuple<string, string, string, bool>>(); m_definedDateFields = new List<string>(); m_settingsCategory = settingsCategory; m_disposeConnection = true; } #endregion #region [ Properties ] /// <summary> /// Gets reference to <see cref="IRazorEngine"/> used by this <see cref="DataContext"/>. /// </summary> public IRazorEngine RazorEngine => m_razorEngine; /// <summary> /// Gets the <see cref="AdoDataConnection"/> for this <see cref="DataContext"/>, creating a new one if needed. /// </summary> public AdoDataConnection Connection => m_connection ?? (m_connection = new AdoDataConnection(m_settingsCategory)); /// <summary> /// Gets validation pattern and error message for rendered fields, if any. /// </summary> public Dictionary<string, Tuple<string, string>> FieldValidationParameters => m_fieldValidationParameters; /// <summary> /// Gets field value initializers, if any. /// </summary> public List<Tuple<string, string>> FieldValueInitializers => m_fieldValueInitializers; /// <summary> /// Gets any text input fields that should render clickable URLs and e-mail addresses when in view mode. /// </summary> public List<Tuple<string, string, string, bool>> ReadonlyHotLinkFields => m_readonlyHotLinkFields; /// <summary> /// Gets defined date fields, if any. /// </summary> public List<string> DefinedDateFields => m_definedDateFields; /// <summary> /// Gets field name designated for initial focus. /// </summary> public string InitialFocusField => m_initialFocusField; /// <summary> /// Gets or sets the date field razor template file name. /// </summary> public string AddDateFieldTemplate { get { return m_addDateFieldTemplate ?? (m_addDateFieldTemplate = $"{m_razorEngine.TemplatePath}AddDateField.cshtml"); } set { m_addDateFieldTemplate = value; } } /// <summary> /// Gets or sets the input field razor template file name. /// </summary> public string AddInputFieldTemplate { get { return m_addInputFieldTemplate ?? (m_addInputFieldTemplate = $"{m_razorEngine.TemplatePath}AddInputField.cshtml"); } set { m_addInputFieldTemplate = value; } } /// <summary> /// Gets or sets the text area field razor template file name. /// </summary> public string AddTextAreaFieldTemplate { get { return m_addTextAreaFieldTemplate ?? (m_addTextAreaFieldTemplate = $"{m_razorEngine.TemplatePath}AddTextAreaField.cshtml"); } set { m_addTextAreaFieldTemplate = value; } } /// <summary> /// Gets or sets the select field razor template file name. /// </summary> public string AddSelectFieldTemplate { get { return m_addSelectFieldTemplate ?? (m_addSelectFieldTemplate = $"{m_razorEngine.TemplatePath}AddSelectField.cshtml"); } set { m_addSelectFieldTemplate = value; } } /// <summary> /// Gets or sets the check box field razor template file name. /// </summary> public string AddCheckBoxFieldTemplate { get { return m_addCheckBoxFieldTemplate ?? (m_addCheckBoxFieldTemplate = $"{m_razorEngine.TemplatePath}AddCheckBoxField.cshtml"); } set { m_addCheckBoxFieldTemplate = value; } } /// <summary> /// Gets dictionary of per-model custom tokens to use with <see cref="AmendExpressionAttribute"/> values for <see cref="TableOperations{T}"/> in this <see cref="DataContext"/> . /// </summary> /// <remarks> /// The returned dictionary can be used to apply run-time tokens to any defined <see cref="AmendExpressionAttribute"/> values, /// for example, given the following amendment expression applied to a modeled class: /// <code> /// [AmendExpression("TOP {count}", /// TargetExpression = TargetExpression.FieldList, /// AffixPosition = AffixPosition.Prefix, /// StatementTypes = StatementTypes.SelectSet)]] /// public class MyTable /// { /// string MyField; /// } /// </code> /// The per-model key/value pairs could be set as follows at run-time: /// <code> /// int count = 200; /// dataContext.CustomTableOperationTokens[typeof(MyTable)] = new[] { new KeyValuePair<string, string>("{count}", $"{count}") }; /// </code> /// </remarks> public Dictionary<Type, IEnumerable<KeyValuePair<string, string>>> CustomTableOperationTokens => m_customTableOperationTokens ?? (m_customTableOperationTokens = new Dictionary<Type, IEnumerable<KeyValuePair<string, string>>>()); /// <summary> /// Gets table operations cache for data context. /// </summary> internal Dictionary<Type, ITableOperations> TableOperationsCache => m_tableOperationsCache; /// <summary> /// Gets or sets session based primary key cache of table operations, creating it if needed. /// </summary> internal ConcurrentDictionary<Type, DataTable> PrimaryKeySessionCache { get { return m_primaryKeySessionCache ?? (m_primaryKeySessionCache = new ConcurrentDictionary<Type, DataTable>()); } set { m_primaryKeySessionCache = value; } } #endregion #region [ Methods ] /// <summary> /// Releases all the resources used by the <see cref="DataContext"/> object. /// </summary> public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// <summary> /// Releases the unmanaged resources used by the <see cref="DataContext"/> object and optionally releases the managed resources. /// </summary> /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param> protected virtual void Dispose(bool disposing) { if (!m_disposed) { try { if (disposing) { if (m_disposeConnection) m_connection?.Dispose(); } } finally { m_disposed = true; // Prevent duplicate dispose. } } } /// <summary> /// Gets the table operations for the specified modeled table <typeparamref name="TModel"/>. /// </summary> /// <typeparam name="TModel">Modeled database table (or view).</typeparam> /// <returns>Table operations for the specified modeled table <typeparamref name="TModel"/>.</returns> public TableOperations<TModel> Table<TModel>() where TModel : class, new() { IEnumerable<KeyValuePair<string, string>> customTokens = null; if ((object)m_customTableOperationTokens != null) m_customTableOperationTokens.TryGetValue(typeof(TModel), out customTokens); return m_tableOperationsCache.GetOrAdd(typeof(TModel), type => { DataTable primaryKeyCache; TableOperations<TModel> tableOperations = new TableOperations<TModel>(Connection, m_exceptionHandler, customTokens); if (PrimaryKeySessionCache.TryGetValue(type, out primaryKeyCache)) tableOperations.PrimaryKeyCache = primaryKeyCache; return tableOperations; }) as TableOperations<TModel>; } /// <summary> /// Gets the table operations for the specified modeled table type <paramref name="model"/>. /// </summary> /// <param name="model">Modeled database table (or view) type.</param> /// <returns>Table operations for the specified modeled table type <paramref name="model"/>.</returns> public ITableOperations Table(Type model) { IEnumerable<KeyValuePair<string, string>> customTokens = null; if ((object)m_customTableOperationTokens != null) m_customTableOperationTokens.TryGetValue(model, out customTokens); return m_tableOperationsCache.GetOrAdd(model, type => { DataTable primaryKeyCache; ITableOperations tableOperations = Activator.CreateInstance(typeof(TableOperations<>).MakeGenericType(model), Connection, m_exceptionHandler, customTokens) as ITableOperations; // ReSharper disable once PossibleNullReferenceException if (PrimaryKeySessionCache.TryGetValue(type, out primaryKeyCache)) tableOperations.PrimaryKeyCache = primaryKeyCache; return tableOperations; }); } /// <summary> /// Gets the field name targeted as the primary label for the modeled table. /// </summary> /// <typeparam name="TModel">Modeled database table (or view).</typeparam> /// <returns>Field name targeted as the primary label for the modeled table.</returns> public string GetPrimaryLabelField<TModel>() where TModel : class, new() { return GetPrimaryLabelField(typeof(TModel)); } /// <summary> /// Gets the field name targeted as the primary label for the modeled table. /// </summary> /// <param name="model">Modeled database table (or view) type.</param> /// <returns>Field name targeted as the primary label for the modeled table.</returns> public string GetPrimaryLabelField(Type model) { PrimaryLabelAttribute primaryLabelAttribute; if (model.TryGetAttribute(out primaryLabelAttribute) && !string.IsNullOrWhiteSpace(primaryLabelAttribute.FieldName)) return primaryLabelAttribute.FieldName; string[] fieldNames = Table(model).GetFieldNames(false); return fieldNames.Length > 0 ? fieldNames[0] : ""; } /// <summary> /// Gets the field name targeted to mark a record as deleted. /// </summary> /// <typeparam name="TModel">Modeled database table (or view).</typeparam> /// <returns>Field name targeted to mark a record as deleted.</returns> public string GetIsDeletedFlag<TModel>() where TModel : class, new() { return GetIsDeletedFlag(typeof(TModel)); } /// <summary> /// Gets the field name targeted to mark a record as deleted. /// </summary> /// <param name="model">Modeled database table (or view) type.</param> /// <returns>Field name targeted to mark a record as deleted.</returns> public string GetIsDeletedFlag(Type model) { IsDeletedFlagAttribute isDeletedFlagAttribute; if (model.TryGetAttribute(out isDeletedFlagAttribute) && !string.IsNullOrWhiteSpace(isDeletedFlagAttribute.FieldName)) return isDeletedFlagAttribute.FieldName; return null; } /// <summary> /// Determines if user is in a specific role or list of roles (comma separated). /// </summary> /// <param name="securityPrincipal">Principal that provides role-based security.</param> /// <param name="role">Role or comma separated list of roles.</param> /// <returns><c>true</c> if user is in <paramref name="role"/>(s); otherwise, <c>false</c>.</returns> /// <remarks> /// Set to * for any role. /// </remarks> public bool UserIsInRole(SecurityPrincipal securityPrincipal, string role) { if ((object)securityPrincipal == null) return false; if (string.IsNullOrWhiteSpace(role)) return false; role = role.Trim(); string[] roles = role.Split(',').Where(value => !string.IsNullOrWhiteSpace(value)).ToArray(); if (roles.Length > 1) return UserIsInRole(securityPrincipal, roles); ISecurityProvider securityProvider = securityPrincipal.Identity.Provider; List<string> userRoles = securityProvider.UserData.Roles ?? new List<string>(); if (role.Equals("*") && userRoles.Count > 0) return true; foreach (string userRole in userRoles) if (userRole.Equals(role, StringComparison.OrdinalIgnoreCase)) return true; return false; } /// <summary> /// Determines if user is in one of the provided of roles. /// </summary> /// <param name="securityPrincipal">Security principal that provides role-based security.</param> /// <param name="roles">List of role names.</param> /// <returns><c>true</c> if user is in one of the <paramref name="roles"/>; otherwise, <c>false</c>.</returns> public bool UserIsInRole(SecurityPrincipal securityPrincipal, string[] roles) { return roles.Any(role => UserIsInRole(securityPrincipal, role)); } /// <summary> /// Determines if user is in a specific group or list of groups (comma separated). /// </summary> /// <param name="securityPrincipal">Security principal that provides role-based security.</param> /// <param name="group">Group or comma separated list of groups.</param> /// <returns><c>true</c> if user is in <paramref name="group"/>(s); otherwise, <c>false</c>.</returns> /// <remarks> /// Set to * for any group. /// </remarks> public bool UserIsInGroup(SecurityPrincipal securityPrincipal, string group) { if ((object)securityPrincipal == null) return false; if (string.IsNullOrWhiteSpace(group)) return false; group = group.Trim(); string[] groups = group.Split(',').Where(value => !string.IsNullOrWhiteSpace(value)).ToArray(); if (groups.Length > 1) return UserIsInGroup(securityPrincipal, groups); ISecurityProvider securityProvider = securityPrincipal.Identity.Provider; List<string> userGroups = securityProvider?.UserData?.Groups ?? new List<string>(); if (group.Equals("*") && userGroups.Count > 0) return true; Lines 1–500 of 1779. Load more
//****************************************************************************************************** // DataContext.cs - Gbtc // // Copyright © 2016, Grid Protection Alliance. All Rights Reserved. // // Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See // the NOTICE file distributed with this work for additional information regarding copyright ownership. // The GPA licenses this file to you under the MIT License (MIT), the "License"; you may not use this // file except in compliance with the License. You may obtain a copy of the License at: // // http://opensource.org/licenses/MIT // // Unless agreed to in writing, the subject software distributed under the License is distributed on an // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the // License for the specific language governing permissions and limitations. // // Code Modification History: // ---------------------------------------------------------------------------------------------------- // 02/01/2016 - J. Ritchie Carroll // Generated original version of source code. // //****************************************************************************************************** using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Data; using System.Linq; using System.Net.Http; using System.Text; using System.Web.Routing; using GSF.Collections; using GSF.ComponentModel.DataAnnotations; using GSF.Data; using GSF.Data.Model; using GSF.Reflection; using GSF.Security; using GSF.Web.Hubs; using RazorEngine.Templating; // ReSharper disable once StaticMemberInGenericType namespace GSF.Web.Model { /// <summary> /// Defines a data context for Razor views. /// </summary> /// <remarks> /// This class is used by views to render HTML form input templates, paged view model configurations, /// provide access to modeled table operations and general database access. /// </remarks> public class DataContext : IDisposable { #region [ Members ] // Constants /// <summary> /// Defines the regular expression used to validate URLs. /// </summary> public const string UrlValidation = UrlValidationAttribute.ValidationPattern; // Fields private AdoDataConnection m_connection; private readonly IRazorEngine m_razorEngine; private readonly Dictionary<Type, ITableOperations> m_tableOperationsCache; private ConcurrentDictionary<Type, DataTable> m_primaryKeySessionCache; private readonly Action<Exception> m_exceptionHandler; private Dictionary<Type, IEnumerable<KeyValuePair<string, string>>> m_customTableOperationTokens; private readonly Dictionary<string, Tuple<string, string>> m_fieldValidationParameters; private readonly List<Tuple<string, string>> m_fieldValueInitializers; private readonly List<Tuple<string, string, string, bool>> m_readonlyHotLinkFields; private readonly List<string> m_definedDateFields; private readonly string m_settingsCategory; private readonly bool m_disposeConnection; private string m_initialFocusField; private string m_addDateFieldTemplate; private string m_addInputFieldTemplate; private string m_addTextAreaFieldTemplate; private string m_addSelectFieldTemplate; private string m_addCheckBoxFieldTemplate; private bool m_disposed; #endregion #region [ Constructors ] /// <summary> /// Creates a new <see cref="DataContext"/>. /// </summary> /// <param name="connection"><see cref="AdoDataConnection"/> to use; defaults to a new connection.</param> /// <param name="disposeConnection">Set to <c>true</c> to dispose the provided <paramref name="connection"/>.</param> /// <param name="razorEngine">Razor engine instance to use for data context; set to <c>null</c> to use default embedded resources instance.</param> /// <param name="exceptionHandler">Delegate to handle exceptions.</param> public DataContext(AdoDataConnection connection = null, bool disposeConnection = false, IRazorEngine razorEngine = null, Action<Exception> exceptionHandler = null) { m_connection = connection; m_razorEngine = razorEngine ?? RazorEngine<CSharpEmbeddedResource>.Default; m_exceptionHandler = exceptionHandler; m_tableOperationsCache = new Dictionary<Type, ITableOperations>(); m_fieldValidationParameters = new Dictionary<string, Tuple<string, string>>(); m_fieldValueInitializers = new List<Tuple<string, string>>(); m_readonlyHotLinkFields = new List<Tuple<string, string, string, bool>>(); m_definedDateFields = new List<string>(); m_settingsCategory = "systemSettings"; m_disposeConnection = disposeConnection || connection == null; } /// <summary> /// Creates a new <see cref="DataContext"/> using the specified <paramref name="settingsCategory"/>. /// </summary> /// <param name="settingsCategory">Setting category that contains the connection settings.</param> /// <param name="razorEngine">Razor engine instance to use for data context; set to <c>null</c> to use default embedded resources instance.</param> /// <param name="exceptionHandler">Delegate to handle exceptions.</param> public DataContext(string settingsCategory, IRazorEngine razorEngine = null, Action<Exception> exceptionHandler = null) { m_razorEngine = razorEngine ?? RazorEngine<CSharpEmbeddedResource>.Default; m_exceptionHandler = exceptionHandler; m_tableOperationsCache = new Dictionary<Type, ITableOperations>(); m_fieldValidationParameters = new Dictionary<string, Tuple<string, string>>(); m_fieldValueInitializers = new List<Tuple<string, string>>(); m_readonlyHotLinkFields = new List<Tuple<string, string, string, bool>>(); m_definedDateFields = new List<string>(); m_settingsCategory = settingsCategory; m_disposeConnection = true; } #endregion #region [ Properties ] /// <summary> /// Gets reference to <see cref="IRazorEngine"/> used by this <see cref="DataContext"/>. /// </summary> public IRazorEngine RazorEngine => m_razorEngine; /// <summary> /// Gets the <see cref="AdoDataConnection"/> for this <see cref="DataContext"/>, creating a new one if needed. /// </summary> public AdoDataConnection Connection => m_connection ?? (m_connection = new AdoDataConnection(m_settingsCategory)); /// <summary> /// Gets validation pattern and error message for rendered fields, if any. /// </summary> public Dictionary<string, Tuple<string, string>> FieldValidationParameters => m_fieldValidationParameters; /// <summary> /// Gets field value initializers, if any. /// </summary> public List<Tuple<string, string>> FieldValueInitializers => m_fieldValueInitializers; /// <summary> /// Gets any text input fields that should render clickable URLs and e-mail addresses when in view mode. /// </summary> public List<Tuple<string, string, string, bool>> ReadonlyHotLinkFields => m_readonlyHotLinkFields; /// <summary> /// Gets defined date fields, if any. /// </summary> public List<string> DefinedDateFields => m_definedDateFields; /// <summary> /// Gets field name designated for initial focus. /// </summary> public string InitialFocusField => m_initialFocusField; /// <summary> /// Gets or sets the date field razor template file name. /// </summary> public string AddDateFieldTemplate { get { return m_addDateFieldTemplate ?? (m_addDateFieldTemplate = $"{m_razorEngine.TemplatePath}AddDateField.cshtml"); } set { m_addDateFieldTemplate = value; } } /// <summary> /// Gets or sets the input field razor template file name. /// </summary> public string AddInputFieldTemplate { get { return m_addInputFieldTemplate ?? (m_addInputFieldTemplate = $"{m_razorEngine.TemplatePath}AddInputField.cshtml"); } set { m_addInputFieldTemplate = value; } } /// <summary> /// Gets or sets the text area field razor template file name. /// </summary> public string AddTextAreaFieldTemplate { get { return m_addTextAreaFieldTemplate ?? (m_addTextAreaFieldTemplate = $"{m_razorEngine.TemplatePath}AddTextAreaField.cshtml"); } set { m_addTextAreaFieldTemplate = value; } } /// <summary> /// Gets or sets the select field razor template file name. /// </summary> public string AddSelectFieldTemplate { get { return m_addSelectFieldTemplate ?? (m_addSelectFieldTemplate = $"{m_razorEngine.TemplatePath}AddSelectField.cshtml"); } set { m_addSelectFieldTemplate = value; } } /// <summary> /// Gets or sets the check box field razor template file name. /// </summary> public string AddCheckBoxFieldTemplate { get { return m_addCheckBoxFieldTemplate ?? (m_addCheckBoxFieldTemplate = $"{m_razorEngine.TemplatePath}AddCheckBoxField.cshtml"); } set { m_addCheckBoxFieldTemplate = value; } } /// <summary> /// Gets dictionary of per-model custom tokens to use with <see cref="AmendExpressionAttribute"/> values for <see cref="TableOperations{T}"/> in this <see cref="DataContext"/> . /// </summary> /// <remarks> /// The returned dictionary can be used to apply run-time tokens to any defined <see cref="AmendExpressionAttribute"/> values, /// for example, given the following amendment expression applied to a modeled class: /// <code> /// [AmendExpression("TOP {count}", /// TargetExpression = TargetExpression.FieldList, /// AffixPosition = AffixPosition.Prefix, /// StatementTypes = StatementTypes.SelectSet)]] /// public class MyTable /// { /// string MyField; /// } /// </code> /// The per-model key/value pairs could be set as follows at run-time: /// <code> /// int count = 200; /// dataContext.CustomTableOperationTokens[typeof(MyTable)] = new[] { new KeyValuePair<string, string>("{count}", $"{count}") }; /// </code> /// </remarks> public Dictionary<Type, IEnumerable<KeyValuePair<string, string>>> CustomTableOperationTokens => m_customTableOperationTokens ?? (m_customTableOperationTokens = new Dictionary<Type, IEnumerable<KeyValuePair<string, string>>>()); /// <summary> /// Gets table operations cache for data context. /// </summary> internal Dictionary<Type, ITableOperations> TableOperationsCache => m_tableOperationsCache; /// <summary> /// Gets or sets session based primary key cache of table operations, creating it if needed. /// </summary> internal ConcurrentDictionary<Type, DataTable> PrimaryKeySessionCache { get { return m_primaryKeySessionCache ?? (m_primaryKeySessionCache = new ConcurrentDictionary<Type, DataTable>()); } set { m_primaryKeySessionCache = value; } } #endregion #region [ Methods ] /// <summary> /// Releases all the resources used by the <see cref="DataContext"/> object. /// </summary> public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// <summary> /// Releases the unmanaged resources used by the <see cref="DataContext"/> object and optionally releases the managed resources. /// </summary> /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param> protected virtual void Dispose(bool disposing) { if (!m_disposed) { try { if (disposing) { if (m_disposeConnection) m_connection?.Dispose(); } } finally { m_disposed = true; // Prevent duplicate dispose. } } } /// <summary> /// Gets the table operations for the specified modeled table <typeparamref name="TModel"/>. /// </summary> /// <typeparam name="TModel">Modeled database table (or view).</typeparam> /// <returns>Table operations for the specified modeled table <typeparamref name="TModel"/>.</returns> public TableOperations<TModel> Table<TModel>() where TModel : class, new() { IEnumerable<KeyValuePair<string, string>> customTokens = null; if ((object)m_customTableOperationTokens != null) m_customTableOperationTokens.TryGetValue(typeof(TModel), out customTokens); return m_tableOperationsCache.GetOrAdd(typeof(TModel), type => { DataTable primaryKeyCache; TableOperations<TModel> tableOperations = new TableOperations<TModel>(Connection, m_exceptionHandler, customTokens); if (PrimaryKeySessionCache.TryGetValue(type, out primaryKeyCache)) tableOperations.PrimaryKeyCache = primaryKeyCache; return tableOperations; }) as TableOperations<TModel>; } /// <summary> /// Gets the table operations for the specified modeled table type <paramref name="model"/>. /// </summary> /// <param name="model">Modeled database table (or view) type.</param> /// <returns>Table operations for the specified modeled table type <paramref name="model"/>.</returns> public ITableOperations Table(Type model) { IEnumerable<KeyValuePair<string, string>> customTokens = null; if ((object)m_customTableOperationTokens != null) m_customTableOperationTokens.TryGetValue(model, out customTokens); return m_tableOperationsCache.GetOrAdd(model, type => { DataTable primaryKeyCache; ITableOperations tableOperations = Activator.CreateInstance(typeof(TableOperations<>).MakeGenericType(model), Connection, m_exceptionHandler, customTokens) as ITableOperations; // ReSharper disable once PossibleNullReferenceException if (PrimaryKeySessionCache.TryGetValue(type, out primaryKeyCache)) tableOperations.PrimaryKeyCache = primaryKeyCache; return tableOperations; }); } /// <summary> /// Gets the field name targeted as the primary label for the modeled table. /// </summary> /// <typeparam name="TModel">Modeled database table (or view).</typeparam> /// <returns>Field name targeted as the primary label for the modeled table.</returns> public string GetPrimaryLabelField<TModel>() where TModel : class, new() { return GetPrimaryLabelField(typeof(TModel)); } /// <summary> /// Gets the field name targeted as the primary label for the modeled table. /// </summary> /// <param name="model">Modeled database table (or view) type.</param> /// <returns>Field name targeted as the primary label for the modeled table.</returns> public string GetPrimaryLabelField(Type model) { PrimaryLabelAttribute primaryLabelAttribute; if (model.TryGetAttribute(out primaryLabelAttribute) && !string.IsNullOrWhiteSpace(primaryLabelAttribute.FieldName)) return primaryLabelAttribute.FieldName; string[] fieldNames = Table(model).GetFieldNames(false); return fieldNames.Length > 0 ? fieldNames[0] : ""; } /// <summary> /// Gets the field name targeted to mark a record as deleted. /// </summary> /// <typeparam name="TModel">Modeled database table (or view).</typeparam> /// <returns>Field name targeted to mark a record as deleted.</returns> public string GetIsDeletedFlag<TModel>() where TModel : class, new() { return GetIsDeletedFlag(typeof(TModel)); } /// <summary> /// Gets the field name targeted to mark a record as deleted. /// </summary> /// <param name="model">Modeled database table (or view) type.</param> /// <returns>Field name targeted to mark a record as deleted.</returns> public string GetIsDeletedFlag(Type model) { IsDeletedFlagAttribute isDeletedFlagAttribute; if (model.TryGetAttribute(out isDeletedFlagAttribute) && !string.IsNullOrWhiteSpace(isDeletedFlagAttribute.FieldName)) return isDeletedFlagAttribute.FieldName; return null; } /// <summary> /// Determines if user is in a specific role or list of roles (comma separated). /// </summary> /// <param name="securityPrincipal">Principal that provides role-based security.</param> /// <param name="role">Role or comma separated list of roles.</param> /// <returns><c>true</c> if user is in <paramref name="role"/>(s); otherwise, <c>false</c>.</returns> /// <remarks> /// Set to * for any role. /// </remarks> public bool UserIsInRole(SecurityPrincipal securityPrincipal, string role) { if ((object)securityPrincipal == null) return false; if (string.IsNullOrWhiteSpace(role)) return false; role = role.Trim(); string[] roles = role.Split(',').Where(value => !string.IsNullOrWhiteSpace(value)).ToArray(); if (roles.Length > 1) return UserIsInRole(securityPrincipal, roles); ISecurityProvider securityProvider = securityPrincipal.Identity.Provider; List<string> userRoles = securityProvider.UserData.Roles ?? new List<string>(); if (role.Equals("*") && userRoles.Count > 0) return true; foreach (string userRole in userRoles) if (userRole.Equals(role, StringComparison.OrdinalIgnoreCase)) return true; return false; } /// <summary> /// Determines if user is in one of the provided of roles. /// </summary> /// <param name="securityPrincipal">Security principal that provides role-based security.</param> /// <param name="roles">List of role names.</param> /// <returns><c>true</c> if user is in one of the <paramref name="roles"/>; otherwise, <c>false</c>.</returns> public bool UserIsInRole(SecurityPrincipal securityPrincipal, string[] roles) { return roles.Any(role => UserIsInRole(securityPrincipal, role)); } /// <summary> /// Determines if user is in a specific group or list of groups (comma separated). /// </summary> /// <param name="securityPrincipal">Security principal that provides role-based security.</param> /// <param name="group">Group or comma separated list of groups.</param> /// <returns><c>true</c> if user is in <paramref name="group"/>(s); otherwise, <c>false</c>.</returns> /// <remarks> /// Set to * for any group. /// </remarks> public bool UserIsInGroup(SecurityPrincipal securityPrincipal, string group) { if ((object)securityPrincipal == null) return false; if (string.IsNullOrWhiteSpace(group)) return false; group = group.Trim(); string[] groups = group.Split(',').Where(value => !string.IsNullOrWhiteSpace(value)).ToArray(); if (groups.Length > 1) return UserIsInGroup(securityPrincipal, groups); ISecurityProvider securityProvider = securityPrincipal.Identity.Provider; List<string> userGroups = securityProvider?.UserData?.Groups ?? new List<string>(); if (group.Equals("*") && userGroups.Count > 0) return true;