i am using Quartz.NET in my ASP.NET web application. i put the following code in a button click handler to make sure that it executes (for testing purposes):
Quartz.ISchedulerFactory factory = new Quartz.Impl.StdSchedulerFactory();
Quartz.IScheduler scheduler = factory.GetScheduler();
Quartz.JobDetail job = new Quartz.JobDetail("job", null, typeof(BackupJob));
Quartz.Trigger trigger = Quartz.TriggerUtils.MakeDailyTrigger(8, 30); // i edit this each time before compilation (for testing purposes)
trigger.StartTimeUtc = Quartz.TriggerUtils.GetEvenSecondDate(DateTime.UtcNow);
trigger.Name = "trigger";
scheduler.ScheduleJob(job, trigger);
scheduler.Start();
here's "BackupJob":
public class BackupJob : IJob
{
public BackupJob()
{
}
public void Execute(JobExecutionContext context)
{
NSG.BackupJobStart();
}
}
my question: why is "BackupJobStart()" not firing? i've used similar code before and it worked fine.
EDIT: #Andy White, i would have it in Application_Start in Global.asax. this doesn't work which is why i moved it to a button click handler to narrow down the problem.
Do you have the Quartz.NET logging hooked up? I once had a problem with a job not executing (I forget why), but once I got the Quartz.NET logging going, the problem was obvious.
It's worth a try (if you're not already doing it):
https://www.quartz-scheduler.net/documentation/quartz-2.x/quick-start.html
http://netcommon.sourceforge.net/
http://netcommon.sourceforge.net/documentation.html
Update: Simply add this to your program.cs to enable console logging:
Common.Logging.LogManager.Adapter = new Common.Logging.Simple.ConsoleOutLoggerFactoryAdapter { Level = Common.Logging.LogLevel.Info};
Maybe it's a problem of time.
I've had the same problem as you, and I live in a country which time is UTC + 2. So, when I set the StartTimeUtc to the trigger, I used DateTime.Now, so the trigger didn't have to fire until two hours later, and I thought it has to be fired in the very moment my code started.
Look carefully the time of the trigger's execution and its StartTime
Another possibility is the way you're running the scheduler. I'm not totally sure, but you may run into problems trying to run a scheduling threads in an ASP.NET application. Putting the SchedulerFactory/Scheduler objects in a button click handler doesn't seem like it would give you the desired results.
You may need to create the scheduler at a more "global" level, so that it can run in the "background" of the application. It might also make sense to move any scheduled work into a separate windows service, so that you don't have to maintain the scheduler in the web app.
When you had success in the past, how were you invoking the scheduler?
In my case, there was an issue with IoC - there were some Interfaces that weren't implemented. I could see what was wrong with mine by adding logging:
Common.Logging.LogManager.Adapter = new Common.Logging.Simple.ConsoleOutLoggerFactoryAdapter { Level = Common.Logging.LogLevel.Info};
to Program.cs as suggested by Andy White
Related
I have came across a requirement where i want axon to wait untill all events in the eventbus fired against a particular Command finishes their execution. I will the brief the scenario:
I have a RestController which fires below command to create an application entity:
#RestController
class myController{
#PostMapping("/create")
#ResponseBody
public String create(
org.axonframework.commandhandling.gateway.CommandGateway.sendAndWait(new CreateApplicationCommand());
System.out.println(“in myController:: after sending CreateApplicationCommand”);
}
}
This command is being handled in the Aggregate, The Aggregate class is annotated with org.axonframework.spring.stereotype.Aggregate:
#Aggregate
class MyAggregate{
#CommandHandler //org.axonframework.commandhandling.CommandHandler
private MyAggregate(CreateApplicationCommand command) {
org.axonframework.modelling.command.AggregateLifecycle.apply(new AppCreatedEvent());
System.out.println(“in MyAggregate:: after firing AppCreatedEvent”);
}
#EventSourcingHandler //org.axonframework.eventsourcing.EventSourcingHandler
private void on(AppCreatedEvent appCreatedEvent) {
// Updates the state of the aggregate
this.id = appCreatedEvent.getId();
this.name = appCreatedEvent.getName();
System.out.println(“in MyAggregate:: after updating state”);
}
}
The AppCreatedEvent is handled at 2 places:
In the Aggregate itself, as we can see above.
In the projection class as below:
#EventHandler //org.axonframework.eventhandling.EventHandler
void on(AppCreatedEvent appCreatedEvent){
// persists into database
System.out.println(“in Projection:: after saving into database”);
}
The problem here is after catching the event at first place(i.e., inside aggregate) the call gets returned to myController.
i.e. The output here is:
in MyAggregate:: after firing AppCreatedEvent
in MyAggregate:: after updating state
in myController:: after sending CreateApplicationCommand
in Projection:: after saving into database
The output which i want is:
in MyAggregate:: after firing AppCreatedEvent
in MyAggregate:: after updating state
in Projection:: after saving into database
in myController:: after sending CreateApplicationCommand
In simple words, i want axon to wait untill all events triggered against a particular command are executed completely and then return to the class which triggered the command.
After searching on the forum i got to know that all sendAndWait does is wait until the handling of the command and publication of the events is finalized, and then i tired with Reactor Extension as well using below but got same results: org.axonframework.extensions.reactor.commandhandling.gateway.ReactorCommandGateway.send(new CreateApplicationCommand()).block();
Can someone please help me out.
Thanks in advance.
What would be best in your situation, #rohit, is to embrace the fact you are using an eventually consistent solution here. Thus, Command Handling is entirely separate from Event Handling, making the Query Models you create eventually consistent with the Command Model (your aggregates). Therefore, you wouldn't necessarily wait for the events exactly but react when the Query Model is present.
Embracing this comes down to building your application such that "yeah, I know my response might not be up to date now, but it might be somewhere in the near future." It is thus recommended to subscribe to the result you are interested in after or before the fact you have dispatched a command.
For example, you could see this as using WebSockets with the STOMP protocol, or you could tap into Project Reactor and use the Flux result type to receive the results as they go.
From your description, I assume you or your business have decided that the UI component should react in the (old-fashioned) synchronous way. There's nothing wrong with that, but it will bite your *ss when it comes to using something inherently eventually consistent like CQRS. You can, however, spoof the fact you are synchronous in your front-end, if you will.
To achieve this, I would recommend using Axon's Subscription Query to subscribe to the query model you know will be updated by the command you will send.
In pseudo-code, that would look a little bit like this:
public Result mySynchronousCall(String identifier) {
// Subscribe to the updates to come
SubscriptionQueryResult<Result> result = QueryGateway.subscriptionQuery(...);
// Issue command to update
CommandGateway.send(...);
// Wait on the Flux for the first result, and then close it
return result.updates()
.next()
.map(...)
.timeout(...)
.doFinally(it -> result.close());
}
You could see this being done in this sample WebFluxRest class, by the way.
Note that you are essentially closing the door to the front-end to tap into the asynchronous goodness by doing this. It'll work and allow you to wait for the result to be there as soon as it is there, but you'll lose some flexibility.
I'm trying to find the way to keep the database updated, but the method which does it consumes a lot of time so I try to create a background task to do it.
I searched for solutions and I read this article of different options to run background processes: https://www.hanselman.com/blog/HowToRunBackgroundTasksInASPNET.aspx
But I don't know what's is the best solution out of those, like I'm trying to execute it outside the application. I found something about creating a Windows Service too, but I don't know how, I didn't manage to find some good examples.
What is the best way to keep my database updated everytime I access the application without losing the time it consumes? If you can help me to see the light I would appreciate that so much.
I'm really happy with FluentScheduler, which is handling all my mission-critical scheduling. As well as firing jobs on a scheduled basis, it can also do them on demand, like so:
// Define your job in its own class
public abstract class MyJob : IJob
{
public void Execute()
{
// Do stuff here...
}
}
// Schedule your job at startup
var runAt = DateTime.Today.AddHours(1); // 1am
if (runAt<DateTime.Now)
runAt = runAt.AddDays(1);
Schedule<MyJob>()
.WithName("My Job Name") // Job name, required for manually triggering
.NonReentrant() // Only allow one instance to run at a time
.ToRunOnceAt(runAt) // First execution date/time
.AndEvery(1).Days().At(runAt.Hour, runAt.Minute); // Run every day at the same time
// To manually trigger your job
ScheduledJobRegistry.RunTaskAsync("My Job Name");
I have the scheduled jobs running in a windows Service and use SignalR as a means of triggering them remotely from an MVC Web App when required.
You can use an async method. Just use a void instead of Task.
public async void LongRunningMethod () {
...
// Insert long running code here
...
}
Then call it and it will execute in the background. Be aware that you can have hidden exceptions without proper were handling.
You can also use Hangfire which is a pretty awesome background task scheduler
Here is an example of using Hangfire to run a daily task
RecurringJob.AddOrUpdate(() => Console.Write("Easy!"), Cron.Daily);
Created a workflow with basic as below.
Created a calss library, used ProgId, set comvisible true and registerd the assembly in the Tridion server.
This is the way i have tested:
Created a component
Finished the activity from the work list.
Navigated to the "Global Work list" and finished the Reviewer activity by myself by choosing the "Back to Author" step and clicked the "Finish" button.
The item is not moved to the author. but when i finish the activity again from the global work list, the item moved to author.
It seems that my code is not performing the activity because i tried removed the below VB script code and tried with the default automatic script code.
' Script for Automatic Activity Content Manager Workflow
FinishActivity "Automatic Activity Finished"
It behaves the same as above. so i decided my code is not worked. Can any one please help on this?
Below is the VBScript I used in the script box of "Back to Author":
Option Explicit
Dim workflowHandler
Set workflowHandler = CreateObject("CoreComponentWorkflow.WorkflowHandler");
If Not workflowHandler Is Nothing Then
Call workflowHandler.MoveBackToActivity(Cstr(CurrentWorkItem.ID, "Create or Edit Component")
End If
Set workflowHandler = Nothing
Below is the C# Code:
public void MoveBackToActivity(string workitemid, string strActivitytoMove)
{
try
{
Session session = new Session();
WorkItem workitem = new WorkItem(new TcmUri("workitemid"), session);
ActivityInstance currentactivity = workitem.Activity as ActivityInstance;
ProcessInstance procInstance = currentactivity.Process as ProcessInstance;
IEnumerable<ActivityInstance> ieActivities = procInstance.Activities
.Select (s => s)
.Where (w => w.Title.IndexOf(strActivitytoMove) !=-1)
.OrderByDescending(w =>w.StartDate);
if (ieActivities != null && ieActivities.Count<ActivityInstance>() > 0)
{
ActivityInstance targetactivity = ieActivities.ElementAt(0);
User lastperformuser = targetactivity.Performers.ElementAt(targetactivity.Performers.Count<User>() - 1);
ActivityFinish finish = new ActivityFinish(targetactivity.FinishMessage, lastperformuser, workitem.Session);
currentactivity.Finish(finish);
}
}
catch (Exception ex)
{
throw ex;
}
}
Be aware that you are using an API that is NOT supported in Automatic Activities. The only processes where you are allowed to use TOM.NET are Event System handlers and Template Building Blocks as documented here.
Automatic Workflow Activities - if not developed with VBScript - must use the CoreService interface.
The good news is that I know for a fact this works - plenty of people got it to work in many implementations. The bad news (for you) is that the error is in your code. Have you tried debugging/step-by-step through your code yet? You can attach to to the workflow process (cm_wf_svc.exe) and figure out what's wrong with the code much faster than we can.
Here's a really simple snippet to finish an activity with CoreService:
ActivityFinishData activityFinish = new ActivityFinishData
{
Message = "Automatically Finished from Expiration Workflow Extension"
};
ActivityInstanceData activityInstance =
(ActivityInstanceData)processInstance.Activities[0];
client.FinishActivity(activityInstance.Id, activityFinish, readOptions);
BTW - If you intended to use TOM.NET anyway, why did you bother asking which API to use?
Following the Nuno's answer, yes you should change the code to use TOM or Core Services. TOM .Net is not supported because it is using a different thread apartment than the underlying technology we use for workflow (COM).
About the issue I have checked that you are calling the activity like this.
Call workflowHandler.MoveBackToActivity(Cstr(CurrentWorkItem.ID, "Create or Edit Component")
It looks like the activity name is not matching. there are some strange characters between "Edit" and "Component"
I hope this helps.
Automatic activities are executed by the Workflow agent service. An Assigned state may indicate that it's just not being picked up by the service. Is your service running correctly, and are things like queue notifications set up properly?
I am running workflows under asp.net and using SynchronizationContext to make the page "wait" for the workflow. Here is how I run the workflow instance under asp.net:
var workflowApplication = new WorkflowApplication(activity);
SynchronizationContext syncContext = SynchronizationContext.Current;
workflowApplication.Completed = delegate { syncContext.OperationCompleted(); };
workflowApplication.SynchronizationContext = syncContext;
syncContext.OperationStarted();
workflowApplication.Run();
In one of the activities I use a bookmark. Now I want the page processing to continue whenever I call CreateBookmark. I tried calling SynchronizationContext.Current.OperationCompleted() before setting the bookmark but that crushes asp.net site when the workflow resumes and completes (I think the workflow instance calls OperationCompleted again when it completes and the error raises)
How can I work with bookmarks under Asp.Net, any ideas?
The Completed Property isn't being called when you persist the workflow with a bookmark, try adding this line after you've set up the completed property:
workflowApplication.PersistableIdle = args => {
syncContext.OperationCompleted();
return PersistableIdleAction.None;
};
This should then be called instead of completed, and the ASP.NET code should regain control. Change the PersistableIdleAction enum to whatever is required, I've just picked None for example code.
I have this code in the asp.net application start evert, and I'm not really familar with the Timer class but what I want to do is have one Trigger that goes off every night at 11 pm, one that goes off at 5:30 in the morning and then every hour after that.
private System.Threading.Timer timer;
protected void Application_Start(object sender, EventArgs e)
{
int intervalMilliseconds = 60 * 1000;
timer = new System.Threading.Timer(new System.Threading.TimerCallback(TimedEvent), null, intervalMilliseconds, intervalMilliseconds);
}
protected void Application_End(object sender, EventArgs e)
{
if (timer != null) timer.Dispose();
}
private void TimedEvent(object stateInfo)
{
MyClass.ExecuteCode();
}
*Please no answers in the way of "don't use asp.net to do triggers because of the lifecycle".
*Again - please no posts on what not to use. I've received two post both telling me what not to use and both not related to my question which is about the Timer class and how to use it.
From your question i'm assuming you don't have full control over your hosting environment, so will try to avoid the schedule it... etc answers.
Having said that, you still need to be aware of the asp.net lifecycle, and your trigger approach is fraught with dangers.
Do you get enough traffic that the application won't end unexpectedly? Do you know the configuration of IIS, so recycling is not a worry?
I can see three approaches:
I would recommend having a page, which uses some sort of key, which is only known
by the caller. Have this page triggered by a watchmouse (See: http://www.watchmouse.com/en/), or scheduled crawler on a pc/server which will always be on, at the times you need it to be triggered.
An alternative would be to trigger a database process, which runs when needed to.
Depending on your environment, this can be scheduled too.
Another would be to check a log file, on users accessing the page, and if it is the first access within the hour, trigger your process. (Do this for whatever period you need.)
However this depends entirely on how heavily your site is accessed, and may not work reliably.
When you create your timer and hook up its tick/elapsed event, set the interval to be every 5 minutes or so.
Then in the tick/elapsed event handler, check the current time and perform an action where necessary. Obviously you will also need to record when an actino has been performed so you don't perform it at 10:58 and 11:03 pm.
Have a look at Quartz.NET, which will allow you to set up cron-like triggers.
Maybe a different way of doing what you want: Instead of relying on ASP to be active, perhaps you can just use the windows scheduler to schedule your event. It has more of the scheduling features you want and will be likely be more reliable as well as already debugged. Your timed event can be as simple as accessing http://localhost/YourApp/.aspx. You'll get the same effect with the added benefit that if your app happens to have recycled, your event will still execute as the 1st request.
You can do the kind of thing you're describing by using the inbuilt ASP.NET Cache.Add CacheItemRemovedCallback delegate. It's a bit of a roundabout way of using it, but you can do effective scheduling this way.
There's an article here showing how to do it.
More information on the CacheItemRemovedCallback here.
Edit: I know you said no services, but if you check the server and find you can use Scheduled Tasks, you can use that to run a console app on a specific schedule like some other other answers mention.