Quartz.net configuration and scheduling - wpf

I have a WPF application where the user creates entities in the database. Each entity has some metadata and an interval field. For each entity I want to create a job with the interval provided and store them in the AdoJobStore.
Now since the WPF app will not always be running, I want a create a Windows Service that reads the jobs data from the AdoJobStore and run those jobs.
So essentially there are these 2 tiers. Now I have setup the Quartz tables already in my existing database. My question is:
How to create/edit/delete jobs from my WPF application
How to inform my windows service to run the jobs (every time an entity is created in database)
I have read through a lot of blogs but these 2 primary questions are a bit unclear to me. I would really appreciate some example code on how to achieve and may be structure my solution.
Thanks

You use Zero Thread Scheduler to schedule jobs. Example scheduler initialization code:
var properties = new NameValueCollection();
properties["quartz.scheduler.instanceId"] = "AUTO";
properties["quartz.threadPool.type"] = "Quartz.Simpl.ZeroSizeThreadPool, Quartz";
properties["quartz.jobStore.type"] = "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz";
properties["quartz.jobStore.driverDelegateType"] = "Quartz.Impl.AdoJobStore.SqlServerDelegate, Quartz";
properties["quartz.jobStore.useProperties"] = "true";
properties["quartz.jobStore.dataSource"] = "default";
properties["quartz.jobStore.tablePrefix"] = tablePrefix;
properties["quartz.jobStore.clustered"] = "false";
properties["quartz.dataSource.default.connectionString"] = connectionString;
properties["quartz.dataSource.default.provider"] = "SqlServer-20";
schedFactory = new StdSchedulerFactory(properties);
BaseScheduler = schedFactory.GetScheduler();
Example scheduling function:
protected ITrigger CreateSimpleTrigger(string tName, string tGroup, IJobDetail jd, DateTime startTimeUtc,
DateTime? endTimeUtc, int repeatCount, TimeSpan repeatInterval, Dictionary<string, string> dataMap,
string description = "")
{
if (BaseScheduler.GetTrigger(new TriggerKey(tName, tGroup)) != null) return null;
var st = TriggerBuilder.Create().
WithIdentity(tName, tGroup).
UsingJobData(new JobDataMap(dataMap)).
StartAt(startTimeUtc).
EndAt(endTimeUtc).
WithSimpleSchedule(x => x.WithInterval(repeatInterval).WithRepeatCount(repeatCount)).
WithDescription(description).
ForJob(jd).
Build();
return st;
}
Obviously, you'll need to provide all relevant fields in your UI and pass values from those fields into the function. Example screenshot of some of the required fields:
Your Windows Service will initialize a Multi Thread Scheduler in OnStart() method in a very similar fashion to the way that Zero Thread Scheduler was initialized above and that Multi Thread Scheduler will monitor all the triggers in your database and start your jobs as specified in those triggers. Quartz.net will do all the heavy lifting in that regard. Once you scheduled your jobs and triggers are in the database all you need to do is initialize that Multi Thread Scheduler, connect it to the database containing triggers and it will keep on firing those jobs and execute your code as long as the service is running.

Related

Dotmim.Sync is throwing exception when synchronizing existing SQLite with SQL Server databases

I get a Dotmim.Sync.SyncException when calling the agent.SynchronizeAsync(tables) function:
Exception: Seems you are trying another Setup tables that what is stored in your server scope database. Please make a migration or create a new scope
This is my code:
public static async Task SynchronizeAsync()
{
var serverProvider = new SqlSyncProvider(serverConnectionString);
// Second provider is using plain old Sql Server provider, relying on triggers and tracking tables to create the sync environment
var clientProvider = new SqliteSyncProvider(Path.Combine(FileSystem.AppDataDirectory, "treesDB.db3"));
// Tables involved in the sync process:
var tables = new string[] { "Trees" };
// Creating an agent that will handle all the process
var agent = new SyncAgent(clientProvider, serverProvider);
// Launch the sync process
var s1 = await agent.SynchronizeAsync(tables);
await agent.LocalOrchestrator.UpdateUntrackedRowsAsync();
var s2 = await agent.SynchronizeAsync();
}
I'm the author of Dotmim.Sync
Do not hesitate to to fill an issue on Github if you are still struggling.
Regarding your issue, I think you have made some tests with different tables.
You need to stick with a set of tables, because DMS needs to create different things (triggers / stored proc and so on)
If you want to test different setups, you need to define differents scopes.
You have a complete documentation on https://dotmimsync.readthedocs.io/

Quartz.net Manually trigger job from Web API

I realize this has sort of been asked before but I want to get a clear confirmation.
I have a Windows Service running the Quartz.Net Scheduler. Jobs and Triggers have been created.
We will have an angular web client that will at times, need to fire jobs manually.
So in a Web API Controller, I have code like this:
var properties = new NameValueCollection
{
["quartz.jobStore.type"] = "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz",
["quartz.jobStore.useProperties"] = "true",
["quartz.jobStore.driverDelegateType"] = "Quartz.Impl.AdoJobStore.SqlServerDelegate, Quartz",
["quartz.jobStore.dataSource"] = "myDS",
["quartz.jobStore.tablePrefix"] = "QRTZ_",
["quartz.dataSource.NAME.provider"] = "SqlServer",
["quartz.dataSource.NAME.connectionString"] = "Server=localhost;Database=QuartzScheduler;Uid=blahuser;Pwd=blahpwd",
["quartz.threadPool.type"] = "Quartz.Simpl.SimpleThreadPool, Quartz"
};
var sched = new StdSchedulerFactory(properties).GetScheduler().Result;
var jobKey = new JobKey("DailyJob1130EST", "DailyGroup");
var jobDataMap = new JobDataMap();
jobDataMap.Add("listIds", "33333");
sched.TriggerJob(jobKey, jobDataMap);
The Job Name and Group do exist in the database.
I was hoping that the call to TriggerJob would cause the Scheduler I have running in my windows service, to fire the job. But it doesn't. Nothing happens, not even an error.
BTW, I don't want to use remoting since it requires the full .NET Framework and the help docs say that it is considered unsafe.
If TriggerJob doesn't work, I guess to run a job manually I'd have to add a new trigger to the scheduler to run once, or something???
There may be other ways, but one way that I was able to successfully do it was:
var sched = await new StdSchedulerFactory(properties).GetScheduler();
var jobKey = new JobKey("DailyJob1130EST", "DailyGroup");
var jobDataMap = new JobDataMap();
jobDataMap.Add("listIds", rInt.ToString());
var trig = TriggerBuilder.Create()
.WithIdentity("RunNowTrigger")
.StartAt(DateBuilder.EvenSecondDate(DateTimeOffset.UtcNow.AddSeconds(5)))
.WithDescription("Run Now Trigger")
.Build();
sched.TriggerJob(jobKey, jobDataMap);
Note: "properties" were my NameValueCollection config information, which I omitted from the sample code. It was nothing out of the ordinary. It just setup the jobStore, dataSource, serializer.type and threadPool.type settings.

How to configure Ignite to work as a full distributed database?

I'm trying to manage a decentralized DB around a huge number of partial DB instances. Each instance has a subset of the whole data and they are all nodes and clients, thus asking for some data the query must be spread to every (group) instance and which one have it will return the data.
Due to avoid lost of data if one instance goes down, I figured out they must replicate its contents with some others. How this scenario can be configured with Ignite?
Supose I have a table with the name and last access datetime of users in a distributed application, like ...
class UserLogOns
{
string UserName;
DateTime LastAccess;
}
Now when the program starts I prepare Ingite for work as a decentralizad DB ...
static void Main(string[] args)
{
TcpCommunicationSpi commSpi = new TcpCommunicationSpi();
// Override local port.
commSpi.LocalPort = 44444;
commSpi.LocalPortRange = 0;
IgniteConfiguration cfg = new IgniteConfiguration();
// Override default communication SPI.
cfg.CommunicationSpi = commSpi;
using (var ignite = Ignition.Start(cfg))
{
var cfgCache = new CacheConfiguration("mio");
cfgCache.AtomicityMode = CacheAtomicityMode.Transactional;
var cache = ignite.GetOrCreateCache<string, UserLogOns>(cfgCache);
cache.Put(Environment.MachineName, new UserLogOns { UserName = Environment.MachineName, LastAccess = DateTime.UtcNow });
}
}
And now ... I want to get LastAccess of other "computerB" when ever it was ..
Is this correct? How can it be implemented?
It depends on the exact use-case that you want to implement. In general, Ignite provides out of the box everything that you mentioned here.
This is a good way to start with using SQL in Ignite: https://apacheignite-sql.readme.io/docs
Create table with "template=partitioned" instead of "replicated" as it is shown in the example here: https://apacheignite-sql.readme.io/docs/getting-started#section-creating-tables, configure number of backups and select a field to be affinity key (a field that is used to map specific entries to cluster node) and just run some queries.
Also check out the concept of baseline topology if you are going to use native persistence: https://apacheignite.readme.io/docs/baseline-topology.
In-memory mode will trigger rebalance between nodes on each server topology change (node that can store data in/out) automatically.

store data in every minute what should use Service, AsyncTask

I want to store data in database in every minute . For the same what should I use Service, AsyncTask or anything else. I go through various link which made me more confused .
I read the developer guide and came to know about getWritableDatabase
Database upgrade may take a long time, you should not call this method from the application main thread,
Then first I think I will use AsyncTask then about this
AsyncTasks should ideally be used for short operations (a few seconds at the most.)
After that I think I can use Service then about Service
A Service is not a thread. It is not a means itself to do work off of the main thread (to avoid Application Not Responding errors).
Here I am not able to understand what should I use to store data in database periodically. Please help me here as struck badly.
Thanks in advance
you cant do a lot work on the UI thread, so making database operations you could choose different approaches, few of them that I prefer to use are listed below;
Create a thread pool and execute each database operation via a thread, this reduces load on UI thread, also it never initializes lot of threads.
You can use services for updating the database operations. since services running on UI thread you cant write your operations in Services, so that you have to create a separate thread inside service method. or you can use Intent service directly since it is not working on UI Thread.
here is developer documentation on thread pool in android
and this is the documentation for IntentService
UPDATE
This will send an intent to your service every minute without using any processor time in your activity in between
Intent myIntent = new Intent(context, MyServiceReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, myIntent, 0);
AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.add(Calendar.SECOND, 60); // first time
long frequency= 60 * 1000; // in ms
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), frequency, pendingIntent);
Before that check if you really need a service to be started in each minute. or if you can have one service which checks for the data changes in each minute, starting new service would consume maybe more resources than checking itself.
UPDATE 2
private ping() {
// periodic action here.
scheduleNext();
}
private scheduleNext() {
mHandler.postDelayed(new Runnable() {
public void run() { ping(); }
}, 60000);
}
int onStartCommand(Intent intent, int x, int y) {
mHandler = new android.os.Handler();
ping();
return STICKY;
}
this is a simple example like that you can do

Setting only the ID in a Linq to SQL object

I am using Linq to SQL in a two tier project where the server tier abstracts the DB and is using Linq to SQL.
In my client tier I construct an object and I send to the server.
I have a Task, which has a relationship to Reporter (who reported this task), so Task has a ReportedID column in the database, which is a FK to Reporter.ID.
In the Linq abstraction, my Task has a Reporter property and a ReportedID property.
To save new Tasks, I would like to use the ReportedID, so I have this code:
//Populate the object with the info
Task task = new Task();
task.Title = tbTitle.Text;
task.Description = tbDescription.Text;
task.Severity = ((Severity)lbSeverities.SelectedItem);
//the first state: "open"
task.StateID = 1;
//TODO - Set ReporterID
task.ReporterID = 1;
//Save the task
client.SaveTaskCompleted += new EventHandler<SaveTaskCompletedEventArgs>(client_SaveTaskCompleted);
client.SaveTaskAsync(App.Token, task);
So, the object is constructed and sent to the server, where it is saved using this code:
public Task SaveTask(string token, Task task)
{
TrackingDataContext dataConext = new TrackingDataContext();
//Saves/Updates the task
dataConext.Tasks.InsertOnSubmit(task);
dataConext.SubmitChanges();
return task;
}
The problem is that I get an exception: "An attempt was made to remove a relationship between a Reporter and a Task. However, one of the relationship's foreign keys (Task.ReporterID) cannot be set to null.".
If I use the Reporter property, it works.
What am I doing wrong?
Thank you,
Oscar
I made some refactory in my code and this error doesn't anymore. It may be a logic error but I can't tell exactly what.

Resources