Beispiel #1
0
        internal BlBinaryExpression ApplySoftDeleteFilterIfApplicable(bool includeDeleted, BlBinaryExpression filter, BlsPawn pawn)
        {
            string           containerName  = Graph.GetStorageContainerNameForPawn(pawn);
            BlGraphContainer container      = Graph.CompiledCollections.FirstOrDefault(c => c.StorageContainerName == containerName);
            BlContainerProp  softDeleteProp = container?.Properties.FirstOrDefault(prop => prop.IsSoftDeleteProp);

            if (softDeleteProp == null)
            {
                return(filter);
            }

            var softDeleteClause = new BlBinaryExpression
            {
                PropName = softDeleteProp.Name, Operator = BlOperator.Eq, Value = includeDeleted
            };

            if (filter == null)
            {
                return(softDeleteClause);
            }

            var newRoot = new BlBinaryExpression
            {
                Left = filter, Operator = BlOperator.And, Right = softDeleteClause
            };

            return(newRoot);
        }
Beispiel #2
0
        public void ResolveContainerMetadataFromPawnSubClass(BlsPawn pawn)
        {
            BlGraphContainer container = new BlGraphContainer
            {
                Properties           = new List <BlContainerProp>(),
                BlContainerName      = pawn.GetType().Name,
                StorageContainerName = _storageNamingEncoder.EncodePawnContainerName(pawn.GetType().Name)
            };

            container = ResolveContainerProps(container, pawn);
            _compiledCollections.Add(container);
        }
Beispiel #3
0
        internal string ResolveSortExpression <TPawn>(Expression <Func <TPawn, IComparable> > sortProperty)
            where TPawn : BlsPawn, new()
        {
            if (sortProperty == null)
            {
                string           containerName       = Graph.GetStorageContainerNameForPawn(new TPawn());
                BlGraphContainer container           = Graph.CompiledCollections.FirstOrDefault(c => c.StorageContainerName == containerName);
                BlContainerProp  defaultSortProperty = container?.Properties.FirstOrDefault(p => p.IsDefaultSort);

                return(defaultSortProperty?.Name);
            }

            Expression expression = sortProperty.Body;

            if (expression is MemberExpression)
            {
                return(expression.ToString().Split('.')[1]);
            }

            throw new InvalidSortPropertyError($"Only property access expression are supported; you provided {expression.GetType().Name}");
        }
Beispiel #4
0
        private BlGraphContainer ResolveContainerProps(BlGraphContainer container, BlsPawn pawn)
        {
            var softDeleteFlagUsed = false;

            List <PropertyInfo> properties = pawn.GetType().GetProperties().ToList();

            foreach (PropertyInfo property in properties)
            {
                Type propType = property.PropertyType;

                if (BlUtils.IsEnumerableType(propType) && propType != typeof(string))
                {
                    throw new DisallowedPawnPropertyError(
                              $"Collection properties are not allowed in pawns: {property.Name} of {pawn.GetType().Name}");
                }

                Attribute[] attributes = property.GetCustomAttributes().ToArray();

                var blProp = new BlContainerProp();
                if (property.CanRead && property.CanWrite)
                {
                    container.Properties.Add(blProp);

                    blProp.Name     = property.Name;
                    blProp.PropType = propType;

                    if (attributes.Any())
                    {
                        foreach (Attribute attribute in attributes)
                        {
                            if (attribute is UsedForSoftDeletes)
                            {
                                if (blProp.PropType != typeof(bool))
                                {
                                    throw new InvalidPropertyTypeForSoftDelete($"Only boolean type is allowed for the soft delete flag. You are trying to apply it to the property {blProp.Name}, which is of type {blProp.PropType}");
                                }
                                if (softDeleteFlagUsed)
                                {
                                    throw new DuplicateSoftDeletionFlagError(
                                              $"Attempting to declare second soft deletion flag in pawn {blProp.Name}. Only one soft deletion property is allowed per pawn");
                                }

                                blProp.IsSoftDeleteProp = true;
                                softDeleteFlagUsed      = true;
                            }

                            if (attribute is FullTextSearchable)
                            {
                                if (blProp.PropType != typeof(string))
                                {
                                    throw new InvalidFullTextSearchAttributeError(
                                              $"Attempting to apply a full text search attribute to a non-string property {blProp.Name} of {container.BlContainerName}");
                                }

                                blProp.IsSearchable = true;
                            }

                            if (attribute is StringLengthRestriction sRes)
                            {
                                if (blProp.PropType != typeof(string))
                                {
                                    throw new InvalidRestrictiveAttributeError(
                                              $"Attempting to apply a string attribute to a non-string property {blProp.Name} of {container.BlContainerName}");
                                }

                                blProp.MinChar = sRes.MinCharacters;
                                blProp.MaxChar = sRes.MaxCharacters == 0 ? int.MaxValue : sRes.MaxCharacters;
                            }

                            if (attribute is NumberRestriction nRes)
                            {
                                if (!BlUtils.IsNumericType(blProp.PropType))
                                {
                                    throw new InvalidRestrictiveAttributeError(
                                              $"Attempting to apply a numeric attribute to a non-number property {blProp.Name} of {container.BlContainerName}");
                                }

                                blProp.MinValue = nRes.Minimum;
                                blProp.MaxValue = Math.Abs(nRes.Maximum) < 0.000001 ? float.MaxValue : nRes.Maximum;
                            }

                            if (attribute is DateRestriction dRes)
                            {
                                if (blProp.PropType != typeof(DateTime))
                                {
                                    throw new InvalidRestrictiveAttributeError(
                                              $"Attempting to apply a date restriction attribute to a non-date property {blProp.Name} of {container.BlContainerName}");
                                }

                                DateTime earliestValue;
                                DateTime latestValue;

                                if (string.IsNullOrEmpty(dRes.Earliest))
                                {
                                    earliestValue = DateTime.MinValue;
                                }
                                else
                                {
                                    var parsed = DateTime.TryParse(dRes.Earliest, out earliestValue);
                                    if (!parsed)
                                    {
                                        throw new InvalidRestrictiveAttributeError(
                                                  $"Date restriction attribute is not in correct format: {blProp.Name} of {container.BlContainerName}");
                                    }
                                }

                                if (string.IsNullOrEmpty(dRes.Latest))
                                {
                                    latestValue = DateTime.MaxValue;
                                }
                                else
                                {
                                    var parsed = DateTime.TryParse(dRes.Latest, out latestValue);
                                    if (!parsed)
                                    {
                                        throw new InvalidRestrictiveAttributeError(
                                                  $"Date restriction attribute is not in correct format: {blProp.Name} of {container.BlContainerName}");
                                    }
                                }

                                blProp.EarliestDate = earliestValue;
                                blProp.LatestDate   = latestValue;
                            }
                        }
                    }
                }
            }

            return(container);
        }
Beispiel #5
0
        /// <summary>
        /// Use this method to retrieve related objects of the certain <typeparam name="T"></typeparam> type. The method
        /// returns a cursor which contains pawns in storage as well as pawns currently sitting in the BLS memory
        /// </summary>
        /// <param name="filter">Optional filter to apply to the result set; the filter will be applied to both the im-memory
        /// and in-storage data</param>
        /// <param name="sortDir"></param>
        /// <param name="includeSoftDeleted">If set to true, the method ignores soft-delete flag if one is available on the
        /// pawn model. Set to false by default</param>
        /// <param name="batchSize">Controls the size of the batch of each incremental retrieval of pawns from storage. Defaults to 200 objects</param>
        /// <param name="sortProperty"></param>
        /// <returns>Cursor containing the result set</returns>
        /// <exception cref="InvalidOperationException"></exception>
        public StorageCursor <T> Find(
            Expression <Func <T, bool> > filter = null,
            Expression <Func <T, IComparable> > sortProperty = null,
            Sort sortDir            = Sort.Asc,
            bool includeSoftDeleted = false,
            int batchSize           = 200)
        {
            Connection[] connections = SourcePawn.SystemRef.ToConnect
                                       .Where(c => c.From == SourcePawn)
                                       .ToArray();

            BlGraphContainer container = SourcePawn.SystemRef.Graph.CompiledCollections
                                         .FirstOrDefault(c => c.BlContainerName == typeof(T).Name);

            if (container == null)
            {
                throw new InvalidOperationException("collection not found");
            }

            string id            = SourcePawn.GetId();
            string relationName  = SourcePawn.SystemRef.Graph.GetStorageRelationName(this);
            string containerName = SourcePawn.SystemRef.Graph.GetStorageContainerNameForPawn(SourcePawn);

            if (filter == null)
            {
                var filterExpression = SourcePawn.SystemRef
                                       .ApplySoftDeleteFilterIfApplicable(includeSoftDeleted, null, new T());

                List <T> connectedPawns = connections.Select(c => (T)c.To).ToList();

                // if there is no id, the pawn has not been saved yet so there only return in-memory relations
                if (string.IsNullOrEmpty(id))
                {
                    return(new StorageCursor <T>().AttachInMemoryPawns(connectedPawns).InjectBls(SourcePawn.SystemRef));
                }

                // otherwise, also check the storage

                string sort = SourcePawn.SystemRef.ResolveSortExpression(sortProperty);

                var cursorFromStorage =
                    SourcePawn.SystemRef.StorageProvider.GetByRelation <T>(id, relationName, containerName, filterExpression, sort, sortDir, batchSize);
                return(cursorFromStorage.AttachInMemoryPawns(connectedPawns).InjectBls(SourcePawn.SystemRef));
            }

            // the rest of the code assumes the filter is not null
            if (string.IsNullOrEmpty(id))
            {
                List <T> foundRelations = connections.Select(c => (T)c.To).ToList();
                List <T> connectedPawns = foundRelations.Where(filter.Compile()).ToList();
                return(new StorageCursor <T>().AttachInMemoryPawns(connectedPawns).InjectBls(SourcePawn.SystemRef));
            }
            else
            {
                List <T>           foundRelations          = connections.Select(c => (T)c.To).ToList();
                List <T>           connectedPawns          = foundRelations.Where(filter.Compile()).ToList();
                BlBinaryExpression storageFilterExpression = SourcePawn.SystemRef.ResolveFilterExpression(filter);

                storageFilterExpression = SourcePawn.SystemRef
                                          .ApplySoftDeleteFilterIfApplicable(includeSoftDeleted, storageFilterExpression, new T());

                string sort = SourcePawn.SystemRef.ResolveSortExpression(sortProperty);

                StorageCursor <T> cursorFromStorage =
                    SourcePawn.SystemRef.StorageProvider.GetByRelation <T>(id, relationName, containerName, storageFilterExpression, sort, sortDir, batchSize);
                return(cursorFromStorage.AttachInMemoryPawns(connectedPawns).InjectBls(SourcePawn.SystemRef));
            }
        }