/// <summary> /// Adds the validation errors. /// </summary> /// <param name="editItem">The edit item.</param> /// <param name="context">The context.</param> private static void AddValidationErrors(IEditableRoot editItem, SynchronizationContext context) { var brokenRules = ((IBusinessObject)editItem).GetAllBrokenRules(true); context.AddError( editItem.Id > 0 ? string.Format(CultureInfo.InvariantCulture, "Item {0} is invalid and cannot be saved.", editItem.Id) : "Item is invalid and cannot be saved."); foreach (var brokenRule in brokenRules.Where(x => !string.IsNullOrEmpty(x.Description))) context.AddError(brokenRule.Description); }
/// <summary> /// Executes the specified context. /// </summary> /// <param name="context">The context.</param> /// <exception cref="System.InvalidOperationException">No key fields were specified.</exception> public void Execute(JobExecutionContext context) { var startTime = DateTime.Now; Ioc.SatisfyImportsOnce(this); var processName = string.Empty; try { processName = GetProcessName(context); var isSuccessful = true; Logger.Log( LogSeverity.Information, "ESYNC", string.Format(CultureInfo.InvariantCulture, "Process \"{0}\" synchronization started.", processName)); var synchronizer = GetProcessSynchronizer(context); var session = CreateESyncSession(startTime, context); try { var filterBuilders = CreateKeyFilterBuilders(synchronizer); if (filterBuilders.Count == 0) throw new InvalidOperationException("No key fields were specified."); var fieldUpdaters = CreateFieldUpdaters(synchronizer); var dpContext = new DataProviderContext(); var lastSuccessfulSession = GetLastSuccessfulSession(synchronizer); if (lastSuccessfulSession != null) dpContext.LastSuccessDate = lastSuccessfulSession.EndTime; var data = GetData(synchronizer.DataProvider, dpContext); session = LogItemsRetrieved(session, data.Count); for (var i = 0; i < data.Count; ++i) { if (SyncServiceJob.IsStopped) { isSuccessful = false; LogError(session, "The synchronization session was stopped."); break; } var item = data[i]; var syncContext = new SynchronizationContext(); try { var result = Synchronize(synchronizer, filterBuilders, fieldUpdaters, item, syncContext); isSuccessful = isSuccessful && result; if (!result || syncContext.Errors.Count > 0 || syncContext.Warnings.Count > 0) { var description = GetDescription(syncContext); Logger.Log( LogSeverity.Error, "ESYNC", string.Format( CultureInfo.InvariantCulture, "Synchronization failed for process \"{0}\", record {1}:{2}{3}", processName, i + 1, Environment.NewLine, description)); LogError( session, string.Format(CultureInfo.InvariantCulture, "Failed to synchronize record {0}.", i + 1), description, item); } } catch (Exception ex) { isSuccessful = false; Logger.Log(LogSeverity.Error, "ESYNC", ex); LogError(session, string.Format(CultureInfo.InvariantCulture, "Failed to synchronize record {0}.", i + 1), ex.ToString()); } } } catch (Exception ex) { isSuccessful = false; Logger.Log(LogSeverity.Error, "ESYNC", ex); LogError(session, ex.Message, ex.ToString()); } if (session != null) { session.EndTime = DateTime.Now; session.IsSuccessful = isSuccessful; ((ISavable)session).Save(); } } catch (Exception ex) { Logger.Log(LogSeverity.Error, GetType().ToString(), ex); } finally { //Csla.ApplicationContext.User = new UnauthenticatedPrincipal(); Logger.Log(LogSeverity.Information, "ESYNC", string.Format(CultureInfo.InvariantCulture, "Process \"{0}\" synchronization finished.", processName)); } }
/// <summary> /// Executes the update. /// </summary> /// <param name="job">The job.</param> /// <param name="itemId">The item identifier.</param> /// <param name="fieldUpdaters">The field updaters.</param> /// <param name="dynamicItem">The dynamic item.</param> /// <param name="context">The context.</param> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> private bool ExecuteUpdate( IProcessSynchronizer job, int itemId, IEnumerable<IFieldUpdater> fieldUpdaters, ISyncDynamicItem dynamicItem, SynchronizationContext context) { try { var criteria = new DetailCriteria(itemId) { AllowLazyLoading = true }; var editItem = DynamicTypeManager.GetEditableRoot<IEditableRoot>(job.ProcessName, criteria); if (editItem == null || editItem.Id != itemId) { context.AddError(string.Format(CultureInfo.InvariantCulture, "Failed to load item with Id = {0}.", itemId)); return false; } foreach (var fieldUpdater in fieldUpdaters) { fieldUpdater.Update(dynamicItem, editItem); if (fieldUpdater.IsKey) { editItem.KeyFields.Add(fieldUpdater.FieldName); } } if (!((ITrackStatus)editItem).IsValid) { AddValidationErrors(editItem, context); return false; } ((ISavable)editItem).Save(); return true; } catch (Exception ex) { context.AddException(ex); } return false; }
/// <summary> /// Updates the items. /// </summary> /// <param name="job">The job.</param> /// <param name="itemsToSynchronize">The items to synchronize.</param> /// <param name="fieldUpdaters">The field updaters.</param> /// <param name="dynamicItem">The dynamic item.</param> /// <param name="context">The context.</param> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> private bool UpdateItems( IProcessSynchronizer job, IEnumerable<int> itemsToSynchronize, IEnumerable<IFieldUpdater> fieldUpdaters, ISyncDynamicItem dynamicItem, SynchronizationContext context) { return itemsToSynchronize.Aggregate(true, (current, id) => current && ExecuteUpdate(job, id, fieldUpdaters, dynamicItem, context)); }
/// <summary> /// Executes the insert. /// </summary> /// <param name="job">The job.</param> /// <param name="fieldUpdaters">The field updaters.</param> /// <param name="dynamicItem">The dynamic item.</param> /// <param name="context">The context.</param> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> private bool ExecuteInsert( IProcessSynchronizer job, IEnumerable<IFieldUpdater> fieldUpdaters, ISyncDynamicItem dynamicItem, SynchronizationContext context) { try { var editItem = DynamicTypeManager.NewEditableRoot<IEditableRoot>(job.ProcessName); editItem.AllowLazyLoading = true; foreach (var fieldUpdater in fieldUpdaters) { fieldUpdater.Update(dynamicItem, editItem); if (fieldUpdater.IsKey) { editItem.KeyFields.Add(fieldUpdater.FieldName); } } if (!((ITrackStatus)editItem).IsValid) { AddValidationErrors(editItem, context); return false; } ((ISavable)editItem).Save(); return true; } catch (Exception ex) { context.AddException(ex); } return false; }
/// <summary> /// Synchronizes the specified job. /// </summary> /// <param name="job">The job.</param> /// <param name="filterBuilders">The filter builders.</param> /// <param name="fieldUpdaters">The field updaters.</param> /// <param name="item">The item.</param> /// <param name="context">The context.</param> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> /// <exception cref="System.ArgumentException">At least one key field should be specified.</exception> /// <exception cref="System.NotSupportedException"></exception> private bool Synchronize( IProcessSynchronizer job, IList<IFieldFilterBuilder> filterBuilders, IList<IFieldUpdater> fieldUpdaters, ProviderItem item, SynchronizationContext context) { if (filterBuilders.Count == 0) throw new ArgumentException("At least one key field should be specified."); var failedAttempts = 0; Random random = null; while (true) { try { using (var transactionScope = TransactionUtils.CreateTransactionScope()) { var dynamicItem = job.CreateDynamicItem(item); var filterList = filterBuilders.Select(filterBuilder => filterBuilder.GetFilter(dynamicItem)).ToList(); var itemsToSynchronize = RuntimeDatabase.FindItems(job.ProcessName, filterList); bool isSuccessful; switch (job.Action) { case ESyncAction.Insert: isSuccessful = itemsToSynchronize.Count > 0 || ExecuteInsert(job, fieldUpdaters, dynamicItem, context); break; case ESyncAction.Update: isSuccessful = UpdateItems(job, itemsToSynchronize, fieldUpdaters, dynamicItem, context); break; case ESyncAction.InsertOrUpdate: isSuccessful = itemsToSynchronize.Count > 0 ? UpdateItems(job, itemsToSynchronize, fieldUpdaters, dynamicItem, context) : ExecuteInsert(job, fieldUpdaters, dynamicItem, context); break; default: throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture, "Action \"{0}\" is not supported.", job.Action)); } if (isSuccessful) transactionScope.Complete(); return isSuccessful; } } catch (Exception ex) { ++failedAttempts; if (failedAttempts < MaximumAttempts && CanRetry(ex)) { Logger.Log(LogSeverity.Error, "eSync", ex); Logger.Log(LogSeverity.Warning, "eSync", "A recoverable error occurred. The transaction will be rerun."); if (random == null) { random = new Random(Thread.CurrentThread.ManagedThreadId); } var delay = MinimumRetryDelay + TimeSpan.FromSeconds((MaximumRetryDelay - MinimumRetryDelay).TotalSeconds * random.NextDouble()); Thread.Sleep(delay); } else { throw; } } } }
/// <summary> /// Gets the description. /// </summary> /// <param name="context">The context.</param> /// <returns>System.String.</returns> private static string GetDescription(SynchronizationContext context) { var sb = new StringBuilder(); if (context.Errors.Count > 0) { sb.AppendFormat(CultureInfo.InvariantCulture, "Errors:").AppendLine(); foreach (var errorMessage in context.Errors) { sb.AppendFormat(CultureInfo.InvariantCulture, "- {0}", errorMessage).AppendLine(); } } if (context.Warnings.Count > 0) { if (sb.Length > 0) sb.AppendLine(); sb.AppendFormat(CultureInfo.InvariantCulture, "Warnings:").AppendLine(); foreach (var warningMessage in context.Warnings) { sb.AppendFormat(CultureInfo.InvariantCulture, "- {0}", warningMessage).AppendLine(); } } if (context.Exceptions.Count > 0) { if (sb.Length > 0) sb.AppendLine(); sb.AppendFormat(CultureInfo.InvariantCulture, "Exceptions:").AppendLine(); foreach (var exception in context.Exceptions) { sb.AppendLine(exception.ToString()); } } return sb.ToString(); }