Exemplo n.º 1
0
        /// <summary>
        ///
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="propertyInfo"></param>
        /// <returns></returns>
        internal static ClassProperty GetProperty <TEntity>(PropertyInfo propertyInfo)
            where TEntity : class
        {
            if (propertyInfo == null)
            {
                return(null);
            }

            // Variables
            var properties = PropertyCache.Get <TEntity>();
            var name       = PropertyMappedNameCache.Get(propertyInfo);

            // Failing at some point - for base interfaces
            var property = properties
                           .FirstOrDefault(p =>
                                           string.Equals(p.GetMappedName(), name, StringComparison.OrdinalIgnoreCase));

            // Matches to the actual class properties
            if (property == null)
            {
                property = properties
                           .FirstOrDefault(p =>
                                           string.Equals(p.PropertyInfo.Name, name, StringComparison.OrdinalIgnoreCase));
            }

            // Return the value
            return(property);
        }
Exemplo n.º 2
0
        // Static Methods

        #region Parse (Expression)

        /// <summary>
        /// Parse an instance of <see cref="BinaryExpression"/> object.
        /// </summary>
        /// <typeparam name="TEntity">The target entity type</typeparam>
        /// <param name="expression">The instance of <see cref="BinaryExpression"/> to be parsed.</param>
        /// <returns>An instance of <see cref="QueryField"/> object.</returns>
        internal static QueryField Parse <TEntity>(BinaryExpression expression) where TEntity : class
        {
            // Only support the following expression type
            if (expression.IsExtractable() == false)
            {
                throw new NotSupportedException($"Expression '{expression.ToString()}' is currently not supported.");
            }

            // Name
            var fieldName = expression.GetName();

            if (PropertyCache.Get <TEntity>().Any(property => PropertyMappedNameCache.Get(property.PropertyInfo, false) == fieldName) == false)
            {
                throw new InvalidQueryExpressionException($"Invalid expression '{expression.ToString()}'. The property {fieldName} is not defined on a target type '{typeof(TEntity).FullName}'.");
            }

            // Value
            var value = expression.GetValue();

            // Operation
            var operation = GetOperation(expression.NodeType);

            // Return the value
            return(new QueryField(fieldName, operation, value));
        }
Exemplo n.º 3
0
        /// <summary>
        /// Parses a dynamic object and convert back the result to an instance of <see cref="QueryGroup"/> object.
        /// </summary>
        /// <param name="obj">The dynamic object to be parsed.</param>
        /// <returns>An instance of the <see cref="QueryGroup"/> with parsed properties and values.</returns>
        public static QueryGroup Parse(object obj)
        {
            // Check for value
            if (obj == null)
            {
                throw new ArgumentNullException($"Parameter 'obj' cannot be null.");
            }

            // Type of the object
            var type = obj.GetType();

            // Check if it is a generic type

            if (type.IsGenericType == false)
            {
                throw new InvalidOperationException("Only dynamic object is supported in the 'where' expression.");
            }

            // Declare variables
            var fields = new List <QueryField>();

            // Iterate every property
            foreach (var property in type.GetProperties())
            {
                var value = property.GetValue(obj);
                fields.Add(new QueryField(PropertyMappedNameCache.Get(property, true), value));
            }

            // Return
            return(fields != null ? new QueryGroup(fields).Fix() : null);
        }
Exemplo n.º 4
0
 /// <summary>
 /// Parse an object and creates an enumerable of <see cref="Field"/> objects. Each field is equivalent
 /// to each property of the given object. The parse operation uses a reflection operation.
 /// </summary>
 /// <param name="obj">An object to be parsed.</param>
 /// <returns>An enumerable of <see cref="Field"/> objects.</returns>
 internal static IEnumerable <Field> Parse(object obj)
 {
     foreach (var property in obj.GetType().GetProperties())
     {
         yield return(new Field(PropertyMappedNameCache.Get(property)));
     }
 }
Exemplo n.º 5
0
 /// <summary>
 /// Gets the mapped-name for the current property.
 /// </summary>
 /// <returns>The mapped-name value.</returns>
 public string GetMappedName()
 {
     if (mappedName != null)
     {
         return(mappedName);
     }
     return(mappedName = PropertyMappedNameCache.Get(GetDeclaringType(), PropertyInfo));
 }
Exemplo n.º 6
0
 /// <summary>
 /// Gets the quoted mapped-name for the current property.
 /// </summary>
 /// <returns>The quoted mapped-name value.</returns>
 public string GetQuotedMappedName()
 {
     if (m_quotedMappedName != null)
     {
         return(m_quotedMappedName);
     }
     return(m_quotedMappedName = PropertyMappedNameCache.Get(PropertyInfo, true, DbSetting));
 }
Exemplo n.º 7
0
 /// <summary>
 /// Gets the mapped-name for the current property.
 /// </summary>
 /// <returns>The mapped-name value.</returns>
 public string GetMappedName()
 {
     if (m_mappedName != null)
     {
         return(m_mappedName);
     }
     return(m_mappedName = PropertyMappedNameCache.Get(PropertyInfo));
 }
Exemplo n.º 8
0
 /// <summary>
 /// Gets the unquoted mapped-name for the current property.
 /// </summary>
 /// <returns>The unquoted mapped-name value.</returns>
 public string GetUnquotedMappedName()
 {
     if (m_unquotedMappedName != null)
     {
         return(m_unquotedMappedName);
     }
     return(m_unquotedMappedName = PropertyMappedNameCache.Get(PropertyInfo, false));
 }
Exemplo n.º 9
0
        /// <summary>
        /// Gets the cached <see cref="ClassProperty"/> object of the data entity (via <see cref="PropertyInfo"/> object).
        /// </summary>
        /// <param name="entityType">The type of the data entity.</param>
        /// <param name="propertyInfo">The instance of the <see cref="PropertyInfo"/> object.</param>
        /// <returns>The instance of cached <see cref="ClassProperty"/> object.</returns>
        internal static ClassProperty Get(Type entityType,
                                          PropertyInfo propertyInfo)
        {
            // Validate the presence
            ThrowNullReferenceException(propertyInfo, "PropertyInfo");

            // Return the value
            return(Get(entityType)?
                   .FirstOrDefault(p => p.PropertyInfo == propertyInfo ||
                                   string.Equals(p.GetMappedName(), PropertyMappedNameCache.Get(entityType, propertyInfo), StringComparison.OrdinalIgnoreCase)));
        }
Exemplo n.º 10
0
        /// <summary>
        ///
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="expression"></param>
        /// <param name="isNot"></param>
        /// <param name="isEqualsTo"></param>
        /// <returns></returns>
        private static QueryGroup ParseContainsOrStartsWithOrEndsWithForStringProperty <TEntity>(MethodCallExpression expression,
                                                                                                 bool isNot,
                                                                                                 bool isEqualsTo)
            where TEntity : class
        {
            // TODO: Refactor this

            // Return null if there is no any arguments
            if (expression.Arguments?.Any() != true)
            {
                return(null);
            }

            // Get the value arg
            var value = Convert.ToString(expression.Arguments.FirstOrDefault()?.GetValue());

            // Make sure it has a value
            if (string.IsNullOrWhiteSpace(value))
            {
                throw new NotSupportedException($"Expression '{expression.ToString()}' is currently not supported.");
            }

            // Make sure it is a property info
            var member = expression.Object.ToMember().Member;

            if (member.IsPropertyInfo() == false)
            {
                throw new NotSupportedException($"Expression '{expression.ToString()}' is currently not supported.");
            }

            // Get the property
            var property = member.ToPropertyInfo();

            // Make sure the property is in the entity
            if (PropertyCache.Get <TEntity>().FirstOrDefault(p => string.Equals(p.PropertyInfo.Name, property.Name, StringComparison.OrdinalIgnoreCase)) == null)
            {
                throw new InvalidExpressionException($"Invalid expression '{expression.ToString()}'. The property {property.Name} is not defined on a target type '{typeof(TEntity).FullName}'.");
            }

            // Add to query fields
            var operation  = (isNot == isEqualsTo) ? Operation.NotLike : Operation.Like;
            var queryField = new QueryField(PropertyMappedNameCache.Get(property),
                                            operation,
                                            ConvertToLikeableValue(expression.Method.Name, value));

            // Return the result
            return(new QueryGroup(queryField.AsEnumerable()));
        }
Exemplo n.º 11
0
        /// <summary>
        /// Parse an object and creates an enumerable of <see cref="Field"/> objects. Each field is equivalent
        /// to each property of the given object. The parse operation uses a reflection operation.
        /// </summary>
        /// <param name="obj">An object to be parsed.</param>
        /// <returns>An enumerable of <see cref="Field"/> objects.</returns>
        public static IEnumerable <Field> Parse(object obj)
        {
            if (obj == null)
            {
                throw new NullReferenceException("Parameter 'obj' cannot be null.");
            }
            if (obj.GetType().GetTypeInfo().IsGenericType == false)
            {
                throw new InvalidOperationException("Parameter 'obj' must be dynamic type.");
            }
            var properties = obj.GetType().GetTypeInfo().GetProperties();

            if (properties?.Any() == false)
            {
                throw new InvalidOperationException("Parameter 'obj' must have atleast one property.");
            }
            return(properties.Select(property => new Field(PropertyMappedNameCache.Get(property))));
        }
Exemplo n.º 12
0
        /// <summary>
        /// Parse an instance of <see cref="BinaryExpression"/> object.
        /// </summary>
        /// <typeparam name="TEntity">The target entity type</typeparam>
        /// <param name="expression">The instance of <see cref="BinaryExpression"/> to be parsed.</param>
        /// <returns>An instance of <see cref="QueryField"/> object.</returns>
        internal static QueryField Parse <TEntity>(BinaryExpression expression) where TEntity : class
        {
            // Only support the following expression type
            if (expression.IsExtractable() == false)
            {
                throw new NotSupportedException($"Expression '{expression.ToString()}' is currently not supported.");
            }

            // Name
            var field      = expression.GetField();
            var properties = PropertyCache.Get <TEntity>();

            // Failing at some point - for base interfaces
            var property = properties
                           .FirstOrDefault(p =>
                                           string.Equals(PropertyMappedNameCache.Get(p.PropertyInfo), field.Name, StringComparison.OrdinalIgnoreCase));

            // Matches to the actual class properties
            if (property == null)
            {
                property = properties
                           .FirstOrDefault(p =>
                                           string.Equals(p.PropertyInfo.Name, field.Name, StringComparison.OrdinalIgnoreCase));

                // Reset the field
                field = property?.AsField();
            }

            // Check the existence
            if (property == null)
            {
                throw new InvalidExpressionException($"Invalid expression '{expression.ToString()}'. The property {field.Name} is not defined on a target type '{typeof(TEntity).FullName}'.");
            }

            // Value
            var value = expression.GetValue();

            // Operation
            var operation = GetOperation(expression.NodeType);

            // Return the value
            return(new QueryField(field, operation, value));
        }
Exemplo n.º 13
0
        private static QueryGroup ParseAllOrAnyForArrayOrAnyForList <TEntity>(MethodCallExpression expression, bool isNot, bool isEqualsTo) where TEntity : class
        {
            // Return null if there is no any arguments
            if (expression.Arguments?.Any() != true)
            {
                return(null);
            }

            // Get the last property
            var last = expression
                       .Arguments
                       .LastOrDefault();

            // Make sure the last is a member
            if (last == null || last?.IsLambda() == false)
            {
                throw new NotSupportedException($"Expression '{expression.ToString()}' is currently not supported.");
            }

            // Make sure the last is a binary
            var lambda = last.ToLambda();

            if (lambda.Body.IsBinary() == false)
            {
                throw new NotSupportedException($"Expression '{expression.ToString()}' is currently not supported.");
            }

            // Make sure it is a member
            var binary = lambda.Body.ToBinary();

            if (binary.Left.IsMember() == false && binary.Right.IsMember() == false)
            {
                throw new NotSupportedException($"Expression '{expression.ToString()}' is currently not supported. Expression must contain a single condition to any property of type '{typeof(TEntity).FullName}'.");
            }

            // Make sure it is a property
            var member = binary.Left.IsMember() ? binary.Left.ToMember().Member : binary.Right.ToMember().Member;

            if (member.IsPropertyInfo() == false)
            {
                throw new NotSupportedException($"Expression '{expression.ToString()}' is currently not supported.");
            }

            // Make sure the property is in the entity
            var property = member.ToPropertyInfo();

            if (PropertyCache.Get <TEntity>().FirstOrDefault(p => p.PropertyInfo == property) == null)
            {
                throw new InvalidQueryExpressionException($"Invalid expression '{expression.ToString()}'. The property {property.Name} is not defined on a target type '{typeof(TEntity).FullName}'.");
            }

            // Variables needed for fields
            var queryFields = new List <QueryField>();
            var conjunction = Conjunction.And;

            // Support only various methods
            if (expression.Method.Name == "Any")
            {
                conjunction = Conjunction.Or;
            }
            else if (expression.Method.Name == "All")
            {
                conjunction = Conjunction.And;
            }

            // Call the method
            var first  = expression.Arguments.First();
            var values = (object)null;

            // Identify the type of the argument
            if (first.IsNewArray())
            {
                values = first.ToNewArray().GetValue();
            }
            else if (first.IsMember())
            {
                values = first.ToMember().GetValue();
            }

            // Values must be an array
            if (values is Array)
            {
                var operation = QueryField.GetOperation(binary.NodeType);
                foreach (var value in (Array)values)
                {
                    queryFields.Add(new QueryField(PropertyMappedNameCache.Get(property, true), operation, value));
                }
            }

            // Return the result
            return(new QueryGroup(queryFields, null, conjunction, (isNot == isEqualsTo)));
        }
Exemplo n.º 14
0
 /// <summary>
 /// Gets the mapped-name for the current property.
 /// </summary>
 /// <returns>The mapped-name value.</returns>
 public string GetMappedName()
 {
     return(mappedName ??= PropertyMappedNameCache.Get(GetDeclaringType(), PropertyInfo));
 }
        /// <summary>
        /// Selecting data from Sql with Sql IN clause usually requires 1 Parameter for every value, and this result in
        /// safe Sql Queries, but there is a limit of 2100 parameters on a Sql Command.  This method provides a safe
        /// alternative implementation that is highly performant for large data sets using a list of int values (e.g Ids).
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="sqlConnection"></param>
        /// <param name="idList"></param>
        /// <param name="filterFieldName"></param>
        /// <param name="tableName"></param>
        /// <param name="fields"></param>
        /// <param name="orderBy"></param>
        /// <param name="hints"></param>
        /// <param name="cacheKey"></param>
        /// <param name="cacheItemExpiration"></param>
        /// <param name="commandTimeout"></param>
        /// <param name="transaction"></param>
        /// <param name="logTrace"></param>
        /// <param name="cancellationToken"></param>
        /// <param name="cache"></param>
        /// <returns></returns>
        public static async Task <IEnumerable <TEntity> > QueryBulkResultsByIdAsync <TEntity>(
            this SqlConnection sqlConnection,
            IEnumerable <int> idList,
            string filterFieldName           = null,
            string tableName                 = null,
            IEnumerable <Field> fields       = null,
            IEnumerable <OrderField> orderBy = null,
            string hints                        = null,
            string cacheKey                     = null,
            int?cacheItemExpiration             = null,
            int?commandTimeout                  = null,
            IDbTransaction transaction          = null,
            ICache cache                        = null,
            Action <string> logTrace            = null,
            CancellationToken cancellationToken = default
            ) where TEntity : class
        {
            var connection = sqlConnection ?? throw new ArgumentNullException(nameof(sqlConnection));

            var timer = Stopwatch.StartNew();

            Field filterField;

            if (string.IsNullOrWhiteSpace(filterFieldName))
            {
                //Attempt to dynamically resolve the Filter Field as the Identity or Primary Key field (if the field is a Numeric Type)!
                var classProp = IdentityCache.Get <TEntity>() ?? PrimaryCache.Get <TEntity>();
                if (classProp == null || !classProp.PropertyInfo.PropertyType.IsNumericType())
                {
                    throw new ArgumentException(
                              $"The filter field name was not specified and an Int Id could not be dynamically resolved from the Identity or Primary Key properties for the type [{typeof(TEntity).Name}]",
                              nameof(filterFieldName)
                              );
                }

                filterField = new Field(classProp.GetMappedName());
            }
            else
            {
                //If Specified then we use the Filter Field Name specified and attempt to resolve it on the Model!
                filterField = new Field(PropertyMappedNameCache.Get <TEntity>(filterFieldName) ?? filterFieldName);
            }

            var dbTableName = string.IsNullOrWhiteSpace(tableName)
                ? ClassMappedNameCache.Get <TEntity>()
                : tableName;

            //Ensure we have default fields; default is to include All Fields...
            var fieldsList = fields?.ToList();

            var selectFields = fieldsList?.Any() == true
                ? fieldsList
                : FieldCache.Get <TEntity>();

            //Retrieve only the select fields that are valid for the Database query!
            //NOTE: We guard against duplicate values as a convenience.
            var validSelectFields = await connection
                                    .GetValidatedDbFieldsAsync(dbTableName, selectFields.Distinct())
                                    .ConfigureAwait(false);

            var dbSetting = connection.GetDbSetting();

            var query = new QueryBuilder()
                        .Clear()
                        .Select().FieldsFrom(validSelectFields, dbSetting)
                        .From().TableNameFrom(dbTableName, dbSetting).WriteText("data")
                        .WriteText("INNER JOIN STRING_SPLIT(@StringSplitCsvValues, ',') split")
                        .On().WriteText("(data.").FieldFrom(filterField).WriteText("= split.value)")
                        .OrderByFrom(orderBy, dbSetting)
                        .HintsFrom(hints)
                        .End();

            var commandText   = query.GetString();
            var commandParams = new { StringSplitCsvValues = idList.ToCsvString(false) };

            logTrace?.Invoke($"Query: {commandText}");
            logTrace?.Invoke($"Query Param @StringSplitCsvValues: {commandParams.StringSplitCsvValues}");

            await connection.EnsureOpenAsync(cancellationToken : cancellationToken);

            logTrace?.Invoke($"DB Connection Established in: {timer.ToElapsedTimeDescriptiveFormat()}");

            //By creating a View Model of the data we are interested in we can easily query the View
            //  and teh complex many-to-many join is now encapsulated for us in the SQL View...
            var results = await connection.ExecuteQueryAsync <TEntity>(
                commandText,
                commandParams,
                commandType : CommandType.Text,
                commandTimeout : commandTimeout,
                transaction : transaction,
                cancellationToken : cancellationToken,
                cacheKey : cacheKey,
                cacheItemExpiration : cacheItemExpiration,
                cache : cache
                ).ConfigureAwait(false);

            logTrace?.Invoke($"Query Execution Completed in: {timer.ToElapsedTimeDescriptiveFormat()}");

            return(results);
        }
Exemplo n.º 16
0
 /// <summary>
 /// Gets the mapped-name for the current property.
 /// </summary>
 /// <returns>The mapped-name value.</returns>
 public string GetMappedName() =>
 mappedName ??= PropertyMappedNameCache.Get(GetDeclaringType(), PropertyInfo);
Exemplo n.º 17
0
        private static QueryGroup ParseContainsForArrayOrList <TEntity>(MethodCallExpression expression, bool isNot, bool isEqualsTo) where TEntity : class
        {
            // Return null if there is no any arguments
            if (expression.Arguments?.Any() != true)
            {
                return(null);
            }

            // Get the last arg
            var last = expression
                       .Arguments
                       .LastOrDefault();

            // Make sure the last arg is a member
            if (last == null || last?.IsMember() == false)
            {
                throw new NotSupportedException($"Expression '{expression.ToString()}' is currently not supported.");
            }

            // Make sure it is a property info
            var member = last.ToMember().Member;

            if (member.IsPropertyInfo() == false)
            {
                throw new NotSupportedException($"Expression '{expression.ToString()}' is currently not supported.");
            }

            // Get the property
            var property = member.ToPropertyInfo();

            // Make sure the property is in the entity
            if (PropertyCache.Get <TEntity>().FirstOrDefault(p => p.PropertyInfo == property) == null)
            {
                throw new InvalidQueryExpressionException($"Invalid expression '{expression.ToString()}'. The property {property.Name} is not defined on a target type '{typeof(TEntity).FullName}'.");
            }

            // Get the values
            var values = (object)null;

            // Array/List Separation
            if (expression.Object == null)
            {
                // Expecting an array
                values = expression.Arguments.First().GetValue();
            }
            else
            {
                // Expecting a list here
                values = expression.Object.GetValue();

                // Convert to a proper array type
                if ((values is Array) == false)
                {
                    values = values.AsArray();
                }
            }

            // Add to query fields
            var operation  = (isNot == false && isEqualsTo == true) ? Operation.In : Operation.NotIn;
            var queryField = new QueryField(PropertyMappedNameCache.Get(property, true), operation, values);

            // Return the result
            var queryGroup = new QueryGroup(queryField);

            // Set the IsNot value
            queryGroup.SetIsNot(isNot == true && isEqualsTo == false);

            // Return the instance
            return(queryGroup);
        }