Exemplo n.º 1
0
 /// <summary>
 ///     Merges specs from a part of the entity or serve into the current instance.
 /// </summary>
 /// <typeparam name="TPart">The complex part type to evaluate.</typeparam>
 /// <param name="partGetter">Function to get the part instance.</param>
 /// <param name="source"></param>
 public void MergeFrom <TPart>(Func <TEntityOrService, TPart> partGetter, IProvideSpecifications <TPart> source)
 {
     foreach (var specification in source.GetSpecifications())
     {
         Add(a => specification.Evaluate(partGetter(a)), specification.Key, specification.Description);
     }
 }
Exemplo n.º 2
0
 /// <summary>
 ///     Raises a <see cref="RuleException" /> if the entity tested has errors.
 /// </summary>
 public static void ThrowExceptionIfInvalid <TEntity>(
     this IProvideSpecifications <TEntity> specProvider,
     TEntity entity,
     string exceptionMessageText,
     string category = null)
 {
     ThrowExceptionIfInvalid(specProvider.GetSpecifications(), entity, category);
 }
Exemplo n.º 3
0
        /// <summary>
        ///     Aggregates the specifications/rules from another spec provider into the current instance.
        /// </summary>
        /// <param name="specProvider">
        ///     The other specification provider for the entity or service.
        /// </param>
        public SpecProvider <TEntityOrService> Add(IProvideSpecifications <TEntityOrService> specProvider)
        {
            Guard.ArgumentNotNull(specProvider, nameof(specProvider));

            foreach (var spec in specProvider.GetSpecifications())
            {
                _specs.Add(spec);
            }

            return(this);
        }
Exemplo n.º 4
0
        /// <summary>
        ///     Creates a new generic service to add/update valid entities and/or remove them from the application.
        /// </summary>
        /// <param name="messagePipe">Message pipe to dispatch domain events.</param>
        /// <param name="db">The data store to use to persist the entity.</param>
        /// <param name="idProvider">Function to get a new key for a given entity.</param>
        /// <param name="specProvider">
        ///     The custom rule provider to use to validate entities added/updated to the service. By
        ///     default service will use data annotations to build business rules to validate the object.
        /// </param>
        /// <param name="handler">A handler to use to process entities being added/removed or activated through the provider.</param>
        public ProviderLinq(
            IUnitOfWorkLinq <TEntity, TKey> db,
            IKeyProvider <TEntity, TKey> idProvider,
            IMessagePipe messagePipe = null,
            IProvideSpecifications <TEntity> specProvider = null,
            EntityHandler <TEntity, TKey> handler         = null)
        {
            Guard.ArgumentNotNull(db, nameof(db));
            Guard.ArgumentNotNull(messagePipe, nameof(messagePipe));

            Db = db;

            MessagePipe = messagePipe ?? new NoOpMessagePipe();
            _idProvider = idProvider;

            // wrap processors
            // ----------------------------------------------------
            _onBeforeDelete = new Stack <Action <TKey> >();
            _onBeforeInsert = new Stack <Action <TEntity> >();
            _onInitialize   = new Stack <Action <TEntity> >();

            if (handler != null)
            {
                if (handler.RemoveHandler != null)
                {
                    OnRemoving(handler.RemoveHandler.Handle);
                }

                if (handler.AddHandler != null)
                {
                    OnAdding(handler.AddHandler.Handle);
                }

                if (handler.ActivateHandler != null)
                {
                    OnInitializing(handler.ActivateHandler.Handle);
                }
            }
            // ---------------------------------------------------

            //assign specs
            // ReSharper disable once ConvertIfStatementToConditionalTernaryExpression
            if (specProvider != null)
            {
                _specs = specProvider.GetSpecifications().ToList();
            }
            else
            {
                _specs = _defaultSpecs;
            }
        }
        /// <summary>
        ///     Loads data from the incoming data stream, controls validating of incoming objects and calls commit when all valid
        ///     objects have been loaded.
        /// </summary>
        /// <returns>The load process result state.</returns>
        public ILoaderState Process()
        {
            if (Processor == null)
            {
                throw new InvalidOperationException("Mapper action has not been assigned.");
            }

            if (Commit == null)
            {
                throw new InvalidOperationException("Commit action has not been assigned.");
            }

            // start time will be assigned
            var loaderState = new LoaderState <TLoadStateData>();

            CurrentLoaderState = loaderState;

            // ReSharper disable once SuggestVarOrType_Elsewhere
            var exceptions = loaderState.ErrorsCount;

            try
            {
                loaderState.CurrentRow = 0;

                // the rules to validate incoming data
                // ReSharper disable once SuggestVarOrType_Elsewhere
                ISpecification <TEntityDtoIn>[] rules = _validator.GetSpecifications().ToArray();

                // initialize lookups only once
                if (!_isInitialized)
                {
                    Initialize?.Invoke();

                    _isInitialized = true;
                }

                IEnumerable <TEntityDtoIn> dataSource = EntitiesGet;
                if (dataSource == null)
                {
                    throw new InvalidOperationException("'EntitiesGet' is null.");
                }

                var currentBatch = 0;

                int maxBatchSize = MaxBatchSize ?? DefaultMaxBatchSize;
                if (maxBatchSize < 1)
                {
                    Log.Warn(
                        $"Fixing up max batch size as the supplied value is less than 1. Current value is {MaxBatchSize} which is being set to {DefaultMaxBatchSize}.");

                    maxBatchSize = DefaultMaxBatchSize;
                }

                Log.Info($"Processing {typeof(TEntityDtoIn).Name} in batches of {maxBatchSize}.");

                // split stream into batches of 4
                foreach (IEnumerable <TEntityDtoIn> batch in dataSource.BatchEx(maxBatchSize))
                {
                    currentBatch++;

                    // log the number of batches to enhance testability
                    loaderState.Batches = currentBatch;

                    Log.Info($"Starting to process batch {currentBatch}.");

                    // create new state for each batch
                    loaderState.Valid = new TLoadStateData();

                    foreach (TEntityDtoIn dto in batch)
                    {
                        loaderState.CurrentRow++;

                        try
                        {
                            // always validate incoming data
                            // ReSharper disable once SuggestVarOrType_Elsewhere
                            var errors = rules.ToErrors(dto).ToCollection();
                            if (!errors.Any())
                            {
                                // map + load valid state
                                Processor(loaderState, dto);
                            }
                            else
                            {
                                this.Log.Error(new RuleException(
                                                   $"Can't process row {loaderState.CurrentRow} due to one or more validation errors. Please see the log for more details.",
                                                   errors));

                                loaderState.ErrorsCount++;
                            }
                        }
                        catch (RuleException rex)
                        {
                            Log.Error(
                                $"Can't process row {loaderState.CurrentRow} due to one or more errors. Please see the log for more details.");

                            foreach (Error error in rex.Errors)
                            {
                                Log.Error(error.Message);
                            }

                            loaderState.ErrorsCount++;
                        }
                        catch (ApplicationException apex)
                        {
                            Log.Error(apex, $"Can't process row {loaderState.CurrentRow} due to error: {apex.Message}");

                            loaderState.ErrorsCount++;
                        }
                        catch (Exception ex)
                        {
                            Log.Error(ex, $"Can't process row {loaderState.CurrentRow} due to an unexpected error.");

                            loaderState.ErrorsCount++;
                        }
                    }

                    Log.Info($"Batch {currentBatch} completed.");

                    Log.Info("Committing changes.");

                    // update target
                    Commit(loaderState);

                    Log.Info("Committed changes.");
                }
            }
            finally
            {
                loaderState.EndTime = DateTime.UtcNow;

                Log.Info(loaderState.ToString());
            }

            return(loaderState);
        }