Übersetzungen

"ba98d63f-8b15-4b68-852f-102cbeb70d6e";"Service hinzufügen (Massenverarbeitung)";"Add service (Mass worker)";"";"";"";"False";"0"
"281e4152-6d03-40b4-9971-10728e673a00";"Services hinzufügen";"Add services";"";"";"";"False";"0"
"61bca157-96a6-436b-8719-0521b98462b9";"Starte Prozess";"Start process";"";"";"";"False";"0"
"227b79dd-31f2-4f6d-a255-94eb8aca0e89";"Neustart Prozess";"Restart process";"";"";"";"False";"0"
"03a9ae68-226f-4f19-91cd-47fa4370230b";"{0} zu verarbeiten";"{0} to process";"";"";"";"False";"0"
"e3d32867-83eb-4514-abec-c7e596ae192b";"{0} von {1} verarbeitet";"{0} of {1} processed";"";"";"";"False";"0"
"1cba5efa-7842-4764-abb8-05e2fab5bde3";"Maschine {0} wird verarbeitet.";"Engine {0} processing";"";"";"";"False";"0"
"0dd299df-c558-44fd-86ec-b3b2acd47530";"Keine Bearbeitungrechte für {0}";"No edit rights for {0}";"";"";"";"False";"0"
"cf6889ab-e880-42c9-8755-bfb15f9f01af";"Temporärer Datensatz wurde nicht gefunden: {0} / {1}";"Temporary record not found: {0} / {1}";"";"";"";"False";"0"
"160c2db4-e013-4569-8ab1-a37bdf776707";"Meine Ausnahme";"My exception";"";"";"";"False";"0"

Hintergrundprozess

Worker

public class AddServiceWorker : WorkItemBase
{
    public Guid TaskExecutionId { get; set; }
    public string Remark { get; set; }
    public DateTime Date { get; set; }
    public int MissingRights { get; set; }

    public AddServiceWorker(): base()
    {
        ExpectedRunTime = EnumExpectedRunTime.Long;
    }

    protected override void Run()
    {
        IQueryable<OrmTempSelectedRecords> query = Api.ORM.GetQuery<OrmTempSelectedRecords>(UnitOfWork);
        query = query.Where(ff => ff.TaskExecutionId == TaskExecutionId);

        if (MaxProgress == 0)
            MaxProgress = query.Count();

        IQueryable<Guid> oids = query.Select(ff => ff.SelectedRecordOid);
        IQueryable<OrmEngine> engines = Api.ORM.GetQuery<OrmEngine>(UnitOfWork).Where(ff => oids.Contains(ff.Oid));
        foreach (OrmEngine engine in engines)
        {
            if (engine.IsAllowed(EnumTableOperations.Edit))
            {
                OrmSubEngineServices service = engine.Services.AddNewObject();
                service.SortOrder = engine.Services.Count() - 1;
                service.ServiceDate = Date;
                service.Remark = Remark;
                engine.Save();
            }
            else
                MissingRights++;

            query.Where(ff => ff.SelectedRecordOid == engine.Oid).FirstOrDefault()?.Delete();

            if (CurrentProgress == 2 && NumberOfStarts == 1)
                throw new Exception(Logger.Translate("160C2DB4-E013-4569-8AB1-A37BDF776707"));

            CurrentProgress++;
            Save();
        }
    }

    protected override void WorkItemFinished()
    {
        if (State == EnumWorkItemState.Finished)
            if (MissingRights == 0)
                Api.ClientCommunication.CreateSuccess(Api.User.CurrentUserGuid(), "dd1ceadc-6808-4583-b6b7-dda73188b5a8".Translate(CurrentProgress));
            else
                Api.ClientCommunication.CreateSuccess(Api.User.CurrentUserGuid(), "e3f652e7-8019-4444-9e6c-f3c30528e862".Translate(CurrentProgress - MissingRights, MissingRights));

        base.WorkItemFinished();
    }
}

Igniter-Änderung

Die temporären Datensätze müssen angelegt werden.

CreateTemporaryRecordsIndicator = true;

Anstatt der Verarbeitung, wird nun der Hintergrundprozess erstellt.

public override ActionResult Ignite()
{
    if (SomethingSelected)
    {
        if (IgnitionModel.Parameters.TryGetValue("RemarkMessage", out object remarkObj) && remarkObj is string remark && !string.IsNullOrWhiteSpace(remark))
        {
            DateTime date;
            if (IgnitionModel.Parameters.TryGetValue("ServiceDate", out object dateObj) && dateObj is DateTime)
                date = (DateTime)dateObj;
            else
                date = DateTime.Now;

            Api.Worker.CreateOrUpdate(new AddServiceWorker
            {
                TaskExecutionId = TaskExecutionId,
                Remark = remark,
                Date = date
            });
        }
        else
            Api.ClientCommunication.CreateError(Api.User.CurrentUserGuid(), "d49ef787-3ad1-4f66-bb12-b2660fd71738".Translate());
    }

    return null;
}

Antworten

Warum wurde ein Anwendungsprotokoll geschrieben? Und wie kann man es verhindern?
Der Vorgabewert von LoggingMode ist WorkerLoggingMode.Auto, damit ist dem System gestattet im Zweifelsfall ein Protokoll zu erstellen.
LoggingMode = WorkerLoggingMode.Never würde dies verhindern.

Wurden alle Datensätze korrekt verarbeitet?
Ja. Da der Arbeitsvorrat (die temporäre Tabelle) immer aktuell gehalten wurde. Beim Neustart wurde damit genau an dem Datensatz fortgesetzt bei dem der Fehler auftrat.

Massenverarbeitung

Aktion

[Serializable]
[Toolbox(EnumConfigurationType.NavigationConfigurationGuid, true)]
[ControlFilter("NavigationConfigurationType", ExpressionType.Equal, EnumNavigationConfigurationType.RibbonNavigationGuid, EnumControlFilterApplyState.IfPositive)]
public class ClientActionAddServiceMassWorker : ClientActionGridMassOperationBase, IOrmSecurityHandler, IIgnitableAction
{
    [DisplayName("41ABA083-E37A-4709-98D4-1D685496459C")]
    public string RemarkMessage { get; set; }

    [TokenboxControl]
    [CDPRolesProviderProperties(dataSources: new[] { EnumDataSource.RoleGuid })]
    [DisplayName("BD0D11A9-9E71-4980-9265-C4A037432D48")]
    public RoleSet DialogRoles { get; set; }

    [Browsable(false)]
    public string MessageOnSuccess { get; set; }

    [Browsable(false)]
    public string MessageOnError { get; set; }

    [Browsable(false)]
    public string MessageOnStart { get; set; }

    [Browsable(false)]
    public string ProgressBarProcessName { get; set; }

    [Browsable(false)]
    public string ProgressBarProcessDescription { get; set; }

    public ClientActionAddServiceMassWorker() : base()
    {
        ToolboxName = "BA98D63F-8B15-4B68-852F-102CBEB70D6E";
        Caption = "BA98D63F-8B15-4B68-852F-102CBEB70D6E";
        ControlInitName = "TrainingActionAddServiceMassWorker";
        ToolboxGroupName = "C007681C-8644-4BB0-A4A0-4A643265EABD";
        Id = "D6F15F40-5A3F-4D02-AF5B-491C8705B7BC".ToGuid();
        Icon = "wrench";
        IconName = Icon;

        VisibilityForParentTypes.Add(EnumActionVisibleForParentType.Grid);

        MassOperationIgniter = typeof(OperationFromActionOverSelectedRecordsIgniter).AssemblyQualifiedName;

        DynamicClientVisibility.Add(EnumActionVisibility.IfUserHasRole);
        DynamicClientVisibility.Add(EnumActionVisibility.SomethingSelected);
        SomethingMustBeSelected = true;

        AdditionalClientData.AddOrUpdate("ActionMethodId", "BA.Training.ClientActionAddService");
    }

    public override void AdditionalRibbonButtonAssignment(DevExpress.Web.RibbonButtonItem ribbonItem, EnumActionVisibleForParentType parentType, DevExUIModelBase uiModel = null)
    {
        base.AdditionalRibbonButtonAssignment(ribbonItem, parentType, uiModel);
        AdditionalClientData.AddOrUpdate("RemarkMessage", RemarkMessage);

        AdditionalClientData.AddOrUpdate("DialogAuthorized", Api.User.CurrentUserIsInRole(DialogRoles, false));
    }

    public bool CanHandleOrm(object DataObject, OrmBABase orm)
    {
        bool canHandle;
        if (orm != null)
            canHandle = orm.IsAllowed(EnumTableOperations.Edit);
        else
        {
            OrmEntityConfiguration entityConfig = Api.Config.OrmEntity(EnumDataSourceExtension.Engine.ValueGuid);
            canHandle = entityConfig.IsAllowed(EnumTableOperations.Edit) != EnumTableOperations.Denied;
        }

        AdditionalClientData.AddOrUpdate("UserHasRole", canHandle);
        return canHandle;
    }

    public Type GetWorkItemType()
    {
        return typeof(AddServiceMassWorker);
    }

    public void Ignite(IIgnitableWorkItem task, Guid tempKey, Guid? taskExecutionId, Dictionary<string, object> parameters, JsonFormResult result)
    {
        AddServiceMassWorker worker = (AddServiceMassWorker)task;
        if (parameters.TryGetValue("RemarkMessage", out object remarkObj) && remarkObj is string remark && !string.IsNullOrWhiteSpace(remark))
        {
            DateTime date;
            if (parameters.TryGetValue("ServiceDate", out object dateObj) && dateObj is DateTime)
                date = (DateTime)dateObj;
            else
                date = DateTime.Now;
            worker.Remark = remark;
            worker.Date = date;
        }
    }
}

Erweiterung von EnumLogProcess

namespace BA.Training.Enums.Extensions
{
    [EnumExtension(typeof(EnumLogProcesses))]
    public static class EnumLogProcessesExtension
    {
        public const string AddServiceGuid = "FBF67FBA-9E64-4254-B168-A0C8DF806C63";
        public static readonly EnumLogProcesses AddService = new EnumLogProcesses(AddServiceGuid, 1000, "281E4152-6D03-40B4-9971-10728E673A00");
    }
}

Worker

public class AddServiceMassWorker : MassWorkItem, IIgnitableWorkItem
{
    public Guid TemporaryGuid { get; set; }
    public Guid TaskExecutionId { get; set; }
    public string MessageOnSuccess { get; set; }
    public string MessageOnError { get; set; }
    public string ProgressBarProcessName { get; set; }
    public string ProgressBarProcessDescription { get; set; }
    public IIgnitableAction ClientAction { get; set; }

    public string Remark { get; set; }
    public DateTime Date { get; set; }
    public int MissingRights { get; set; }

    public AddServiceMassWorker() : base()
    {
        ScheduledStartTime = DateTime.UtcNow.AddMinutes(1);
        IsCancellable = true;
        LoggingProcess = EnumLogProcessesExtension.AddService;
        Caption = "281e4152-6d03-40b4-9971-10728e673a00";
        Title = "281e4152-6d03-40b4-9971-10728e673a00";
    }

    protected override IQueryable<OrmBABase> GetQueryable()
    {
        if (NumberOfStarts == 1)
            Logger.AddInfo("61BCA157-96A6-436B-8719-0521B98462B9");
        else
            Logger.AddInfo("227B79DD-31F2-4F6D-A255-94EB8ACA0E89");

        IQueryable<OrmTempSelectedRecords> query = Api.ORM.GetQuery<OrmTempSelectedRecords>(UnitOfWork);
        query = query.Where(ff => ff.TaskExecutionId == TaskExecutionId);

        if (MaxProgress == 0)
        {
            MaxProgress = query.Count();
            Logger.AddInfo("03A9AE68-226F-4F19-91CD-47FA4370230B", MaxProgress);
            throw new Exception(Logger.Translate("160C2DB4-E013-4569-8AB1-A37BDF776707"));
        }
        else
            Logger.AddInfo("E3D32867-83EB-4514-ABEC-C7E596AE192B", CurrentProgress, MaxProgress);

        IQueryable<Guid> oids = query.Select(ff => ff.SelectedRecordOid);
        return Api.ORM.GetQuery<OrmEngine>(UnitOfWork).Where(ff => oids.Contains(ff.Oid));
    }

    protected override void ProcessSingleOrm(OrmBABase ormBABase)
    {
        OrmEngine engine = (OrmEngine)ormBABase;
        Logger.AddInfo("1CBA5EFA-7842-4764-ABB8-05E2FAB5BDE3", engine.Name);

        if (CurrentProgress == 2)
            throw new Exception(Logger.Translate("160C2DB4-E013-4569-8AB1-A37BDF776707"));

        if (engine.IsAllowed(EnumTableOperations.Edit))
        {
            OrmSubEngineServices service = engine.Services.AddNewObject();
            service.SortOrder = engine.Services.Count() - 1;
            service.ServiceDate = Date;
            service.Remark = Remark;
            engine.Save();
        }
        else
        {
            Logger.AddWarning("0DD299DF-C558-44FD-86EC-B3B2ACD47530", engine.Name);
            MissingRights++;
        }

        IQueryable<OrmTempSelectedRecords> query = Api.ORM.GetQuery<OrmTempSelectedRecords>(UnitOfWork);
        query = query.Where(ff => ff.TaskExecutionId == TaskExecutionId && ff.SelectedRecordOid == engine.Oid);
        OrmTempSelectedRecords tempRecord = query.FirstOrDefault();
        if (tempRecord != null)
            tempRecord.Delete();
        else
            Logger.AddWarning("CF6889AB-E880-42C9-8755-BFB15F9F01AF", TaskExecutionId, engine.Oid);
        CurrentProgress++;
    }

    protected override void WorkItemFinished()
    {
        if (State == EnumWorkItemState.Finished)
            if (MissingRights == 0)
            {
                Api.ClientCommunication.CreateSuccess(Api.User.CurrentUserGuid(), "dd1ceadc-6808-4583-b6b7-dda73188b5a8".Translate(CurrentProgress));
                Logger.AddInfo("dd1ceadc-6808-4583-b6b7-dda73188b5a8", CurrentProgress);
            }
            else
            {
                Api.ClientCommunication.CreateError(Api.User.CurrentUserGuid(), "e3f652e7-8019-4444-9e6c-f3c30528e862".Translate(CurrentProgress - MissingRights, MissingRights));
                Logger.AddInfo("e3f652e7-8019-4444-9e6c-f3c30528e862", CurrentProgress - MissingRights, MissingRights);
            }

        base.WorkItemFinished();
    }
}

Antworten

Was steht im Anwendungsprotokoll?

Mass Worker Exception

Wurden alle Datensätze verarbeitet?
Nein, der Datensatz mit der Exception wurde übersprungen.

Warum gibt es einen Unterschied zum vorherigen Hintergrundprozess, und was müsste man tun, um Fehlerhafte Datensätze nochmal zu vearbeiten?
Das MassWorkItem überpringt Datensätze bei denen Ausnahmen auftreten.
Man muss in WorkItemFinished den Worker in dem Fall mit AddSuccessor nochmal starten. Dies macht aber nur Sinn, wenn der Fehler nicht dauerhaft ist.