eXpandFramework / eXpand

DevExpress XAF (eXpressApp) extension framework. 𝗹𝗶𝗻𝗸𝗲𝗱𝗶𝗻.𝗲𝘅𝗽𝗮𝗻𝗱𝗳𝗿𝗮𝗺𝗲𝘄𝗼𝗿𝗸.𝗰𝗼𝗺, 𝘆𝗼𝘂𝘁𝘂𝗯𝗲.𝗲𝘅𝗽𝗮𝗻𝗱𝗳𝗿𝗮𝗺𝗲𝘄𝗼𝗿𝗸.𝗰𝗼𝗺 and 𝘁𝘄𝗶𝘁𝘁𝗲𝗿 @𝗲𝘅𝗽𝗮𝗻𝗱𝗳𝗿𝗮𝗺𝗲𝘄𝗼𝗿𝗸 and or simply 𝗦𝘁𝗮𝗿/𝘄𝗮𝘁𝗰𝗵 this repository and get notified from 𝗚𝗶𝘁𝗛𝘂𝗯
http://expand.expandframework.com
Microsoft Public License
220 stars 114 forks source link

JobScheduler.Hangfire Application Error opening Dashboard #1028

Closed ricardoamm closed 4 months ago

ricardoamm commented 9 months ago

In development mode, when I select a Worker and open dashboard i got the following error, but the dashboard is opened in new tab. In production, the dashboard is not opened,

The error occurred:

Type:       JSException
Message:    Converting circular structure to JSON
--> starting at object with constructor 'Window'
--- property 'window' closes the circle

TypeError: Converting circular structure to JSON --> starting at object with constructor 'Window' --- property 'window' closes the circle at JSON.stringify () at D (https://localhost:5003/_framework/blazor.server.js:1:5345) at https://localhost:5003/_framework/blazor.server.js:1:3541 Data: 0 entries Stack trace:

at Microsoft.JSInterop.JSRuntime.InvokeAsync[TValue](Int64 targetInstanceId, String identifier, Object[] args) InnerException is null

apobekiaris commented 9 months ago

please post a sample and details on how to repro this

ricardoamm commented 7 months ago

Hi, I just select one worker from job and then click on the Dashboard button. record.zip

ricardoamm commented 7 months ago

The issues seems related to invocation "JsRuntime.InvokeAsync(" instead "JsRuntime.InvokeVoidAsync("

apobekiaris commented 7 months ago

did you tried to upgrade the hangfire libs to a newer version to see if they resolved it?

ricardoamm commented 7 months ago

I found the problem in your code on the class Xpand.XAF.Modules.JobScheduler.Hangfire.JobSchedulerService, method JobDashboard.

The return code, should be:

return args.SelectedObjects.Cast().ToObservable(ImmediateScheduler.Instance) .Select(job => jsRuntime?.InvokeVoidAsync("open", $"{uri}{job.Id}", "_blank").AsTask());

Could you confirm, please? It worked for me, but I prefer to reference the Nuggets.

apobekiaris commented 7 months ago

I recently switched the JobScheduler tests to integration tests, for testing the actual app instead of a SUT system, however yet no test for Dashboard. I will add one when my time permits for now your observation looks correct, I will update the source code.

Feel free to send a PR when ever u have any fix that was a nice catch

apobekiaris commented 7 months ago

I do not see how it worked for you as it does not even compile unless you posted a wronf snippet. Without testing the correct version should be

args.SelectedObjects.Cast<JobWorker>().ToNowObservable()
                        .SelectMany(job => Observable.FromAsync(() => jsRuntime?.InvokeVoidAsync("open", [$"{uri}{job.Id}", "_blank"]).AsTask()))

can u please test is fine so I can send a build?

ricardoamm commented 7 months ago

Should be Select instead SelectMany, SelectMany is for relations n to n, which is not the case :

return args.SelectedObjects.Cast<JobWorker>().ToObservable(ImmediateScheduler.Instance).Select(job => jsRuntime?.InvokeVoidAsync("open", $"{uri}{job.Id}", "_blank").AsTask());

apobekiaris commented 7 months ago

your answer beats me, can you explain why you think the Task should not be flattened?

apobekiaris commented 7 months ago

The SelectMany is the operator that flattens the inner observable which in this case is the task. Although this task is executed on creation so it worked as you mentioned with the Select is not the correct approach and probably the ToUnit hide it from you.

IObservable<IObservable<Unit>> observable = e.SelectedObjects.Cast<JobWorker>().ToNowObservable()
                        .Select(job => jsRuntime?.InvokeVoidAsync("open", [$"{uri}{job.Id}", "_blank"]).AsTask().ToObservable());

the Select will project the Task to IObservable<IObservable<Unit>> where the SelectMany will flatten it

IObservable<Unit> observable = e.SelectedObjects.Cast<JobWorker>().ToNowObservable()
                        .SelectMany(job => jsRuntime?.InvokeVoidAsync("open", [$"{uri}{job.Id}", "_blank"]).AsTask().ToObservable());
ricardoamm commented 7 months ago

Ok, maybe I'm not seeing the whole concept here. I'm used to LINQ and I use SelectMany() to collapse a multidimensional sequence in a way that would otherwise require a second Select() or loop. In this case, i thought we just want to select the JobWorker Id's in a simple one-to-one projection, and foreach one create create a task.

apobekiaris commented 7 months ago

I use SelectMany() to collapse a multidimensional sequence

it is LINQ and it is as you said it now else flattening and our return is a Unit not a worker. A Unit is a type we use to merge all observables into a signle subscription

   .Merge(manager.TriggerJobsFromAction())
                    .Merge(manager.PauseJobsFromAction())
                    .Merge(manager.ResumeJobsFromAction())
                    .Merge(manager.JobDashboard())

all this methods same for LINQ must have the same type in order to operate on them (Merge, Concat etc) so we use the Unit, the ToUnit method is a convenient extension that projects any type however if you not carefull it hides the whole picture from you

public static IObservable<Unit> ToUnit<T>(this IObservable<T> source) 
            => source.Select(_ => Unit.Default);
ricardoamm commented 7 months ago

Ok, thanks for the clarification. What is the expected behaviour, o new dashboard tab shoud be opened for each selected Worker? I selected multiple Workers, but only one tab is opened.

apobekiaris commented 7 months ago

The pre-release 4.232.2.0 in the Reactive.XAF lab branch includes commits that relate to this task:

1028](https://github.com/eXpandFramework/Reactive.XAF/commit/663345a48fec111b62b6a6649ddb504c3646acdc)

To minimize version conflicts we recommend that you use the Xpand.XAF.Core.All, Xpand.XAF.Win.All, Xpand.XAF.Web.All packages. Doing so, all packages will be at your disposal and .NET will add a dependecy only to those packages that you actually use and not to all (see the Modules installation-registrations youtube video).

Released packages: Xpand.Extensions v.4.232.2
Xpand.Extensions.Blazor v.4.232.2
Xpand.Extensions.Mono.Cecil v.4.232.2
Xpand.Extensions.Office.Cloud v.4.232.2
Xpand.Extensions.Office.Cloud.Google.Blazor v.4.232.2
Xpand.Extensions.Reactive v.4.232.2
Xpand.Extensions.XAF v.4.232.2
Xpand.Extensions.XAF.Xpo v.4.232.2
Xpand.TestsLib v.4.232.2
Xpand.TestsLib.Blazor v.4.232.2
Xpand.TestsLib.Common v.4.232.2
Xpand.TestsLib.EasyTest v.4.232.2
Xpand.VersionConverter v.4.232.2
Xpand.XAF.Core.All v.4.232.2
Xpand.XAF.Modules.AutoCommit v.4.232.2
Xpand.XAF.Modules.Blazor v.4.232.2
Xpand.XAF.Modules.BulkObjectUpdate v.4.232.2
Xpand.XAF.Modules.CloneMemberValue v.4.232.2
Xpand.XAF.Modules.CloneModelView v.4.232.2
Xpand.XAF.Modules.Email v.4.232.2
Xpand.XAF.Modules.GridListEditor v.4.232.2
Xpand.XAF.Modules.HideToolBar v.4.232.2
Xpand.XAF.Modules.JobScheduler.Hangfire v.4.232.2
Xpand.XAF.Modules.JobScheduler.Notification v.4.232.2
Xpand.XAF.Modules.MasterDetail v.4.232.2
Xpand.XAF.Modules.ModelMapper v.4.232.2
Xpand.XAF.Modules.ModelViewInheritance v.4.232.2
Xpand.XAF.Modules.Office.Cloud.Google v.4.232.2
Xpand.XAF.Modules.Office.Cloud.Google.Calendar v.4.232.2
Xpand.XAF.Modules.Office.Cloud.Google.Tasks v.4.232.2
Xpand.XAF.Modules.Office.DocumentStyleManager v.4.232.2
Xpand.XAF.Modules.OneView v.4.232.2
Xpand.XAF.Modules.PositionInListView v.4.232.2
Xpand.XAF.Modules.ProgressBarViewItem v.4.232.2
Xpand.XAF.Modules.RazorView v.4.232.2
Xpand.XAF.Modules.Reactive v.4.232.2
Xpand.XAF.Modules.Reactive.Logger v.4.232.2
Xpand.XAF.Modules.Reactive.Logger.Client.Win v.4.232.2
Xpand.XAF.Modules.Reactive.Logger.Hub v.4.232.2
Xpand.XAF.Modules.Reactive.Rest v.4.232.2
Xpand.XAF.Modules.RefreshView v.4.232.2
Xpand.XAF.Modules.SequenceGenerator v.4.232.2
Xpand.XAF.Modules.Speech v.4.232.2
Xpand.XAF.Modules.SpellChecker v.4.232.2
Xpand.XAF.Modules.StoreToDisk v.4.232.2
Xpand.XAF.Modules.SuppressConfirmation v.4.232.2
Xpand.XAF.Modules.TenantManager v.4.232.2
Xpand.XAF.Modules.ViewEditMode v.4.232.2
Xpand.XAF.Modules.ViewItemValue v.4.232.2
Xpand.XAF.Modules.ViewWizard v.4.232.2
Xpand.XAF.Modules.Windows v.4.232.2
Xpand.XAF.Web.All v.4.232.2
Xpand.XAF.Win.All v.4.232.2

Please update the related Nuget packages and test if issues is addressed. These are nightly nuget packages available only from our NugetServer.

If you do not use these packages directly but through a module of the main eXpandFramework project, please wait for the bot to notify you again when integration is finished or update the related packages manually.

Thanks a lot for your contribution.

apobekiaris commented 7 months ago

from the RX side we expect a task for each selected object to run

jsRuntime?.InvokeVoidAsync("open", [$"{uri}{job.Id}", "_blank"]).AsTask().ToObservable()
apobekiaris commented 7 months ago

if u want to debug i suggest u verify if they open one by one so to have 2 tabs open at the end, so is it the browser that does not allow multiple popups in very close interval? then maybe a StepInterval operator can help and so on..

apobekiaris commented 7 months ago

or a SelectManySequential operator instead of SelectMany to flatten the task

ricardoamm commented 7 months ago

Thank you very much. When new nuggets will be released?

apobekiaris commented 7 months ago

nugets are already in the lab server, aren't they good enough at least for testing?

ricardoamm commented 7 months ago

sure, I need to add a new package souce to reference them?

apobekiaris commented 7 months ago

strange you can recompile/modify the source and you not aware of the readme? :)

image

did you by any chance find the time to experiement with my last suggestions? SelectManySequential + StepInterval

ricardoamm commented 7 months ago

The browser was blocking multiple popups, so it's ok as it is. I don't know how to specify the stepinterval, but the SelectManySequential also works.

ricardoamm commented 7 months ago

I'm using Hangfire because Windows Workflow Foundation is no longer supported in the new . NET 8. What is the purpose of the JobScheduler Notification Module?

apobekiaris commented 7 months ago

do u mean that it does not work with SelectMany but works with the SelectManaySequential?

ricardoamm commented 7 months ago

work in both ways

apobekiaris commented 7 months ago
return e.SelectedObjects.Cast<JobWorker>().ToNowObservable()
                        .StepInterval(1.Seconds())
                        .SelectManySequential(job => jsRuntime?.InvokeVoidAsync("open", [$"{uri}{job.Id}", "_blank"]).AsTask().ToObservable());

you can add StepInterval just b4 the inner observable what that means is that it will wait for 1 second then it will open the page and it will wait for the task to complete (that's why we flatten) b4 moving to the next item

expand commented 4 months ago

Closing issue for age. Feel free to reopen it at any time.

.Thank you for your contribution.