Recently upgraded my embedded Quartz.net scheduler to 2.x at which point I had to give up on giving my Zero Thread Schedulers unique names and now I have a problem that (very rarely) an object trying to create an instance of ZT scheduler throws an exception because another object already has an instance of ZT scheduler instantiated and since all my ZT schedulers now have the default 'QuartzScheduler' name this throws an exception...
I tried checking for scheduler count using MySchedFactory.AllSchedulers.Count after calling MySchedFactory = new StdSchedulerFactory(properties) but the StdSchedulerFactory creates an instance of ZT scheduler as soon as it's instantiated and not when GetScheduler() method is called so that is a dead end...
I could not find any other way of checking for existing schedulers before instantiating the StdSchedulerFactory and, as I mentioned already, as soon as it is instantiated, it creates an instance of ZT scheduler so I ended up using a while loop in my catch block which is just a horrible solution so I'm hoping someone knows a better way of checking for existing ZT schedulers...
try
{
//setting properties
MySchedFactory = new StdSchedulerFactory(properties);
BaseScheduler = schedFactory.GetScheduler();
}
catch (Exception ex)
{
var exMsg = ex.InnerException == null ?
ex.Message :
ex.Message + Environment.NewLine + ex.InnerException.Message;
while (exMsg.Contains("Scheduler with name 'QuartzScheduler' already exists"))
{
try
{
MySchedFactory = new StdSchedulerFactory(properties);
BaseScheduler = schedFactory.GetScheduler();
}
catch (Exception vex)
{
exMsg = vex.InnerException == null ?
vex.Message :
vex.Message + Environment.NewLine + vex.InnerException.Message;
}
}
}
Any ideas?
How about keeping a reference to the scheduler factory as a singleton instead of creating a new one?
Quartz.net scheduler must be singleton. You can read something here.
Related
I have a program where I add my jobs into a db and schedule them as needed. I used to add my jobs in program.cs as singletons, which works fine but only allows me to add a single job per scheduler and I can't check if it's already there. So I moved all that into one scheduler, where I now check if the job's already in my db and if it isn't then I add it.
It adds the jobs fine but they don't work at all, whenever I try to call one I get a nullreference exception from JobRunShell.Run(). I can't for the life of me figure out why, the job looks exactly the same in the db and I can't find anything wrong with my code. Am I not supposed to add jobs like that?
Program.cs with job added as singleton
builder.Services.AddSingleton<IJobFactory, JobFactory>();
builder.Services.AddSingleton<ISchedulerFactory, StdSchedulerFactory>();
builder.Services.AddSingleton<MailHRNewEmployee>();
builder.Services.AddSingleton(new JobMetadata(Guid.NewGuid(), typeof(MailHRNewEmployee), "HR First Contact", "job blueprint"));
builder.Services.AddHostedService<MyScheduler>();
MyScheduler.cs
public async Task StartAsync(CancellationToken cancellationToken)
{
//Creating Scheduler
Scheduler = await schedulerFactory.GetScheduler();
Scheduler.JobFactory = jobFactory;
//Start Scheduler
await Scheduler.Start(cancellationToken);
//Create Jobs
var istrue = await Scheduler.CheckExists(new JobKey("HR First Contact", "DEFAULT"));
if (!(bool)istrue)
{
jobMetaData = new JobMetadata(Guid.NewGuid(), typeof(MailHRNewEmployee), "HR First Contact", "job blueprint");
IJobDetail jobDetail = CreateJob(jobMetaData);
await Scheduler.AddJob(jobDetail, true);
}
...(checking all the other jobs)
private IJobDetail CreateJob(JobMetadata jobMetadata)
{
return JobBuilder.Create(jobMetaData.JobType).WithIdentity(jobMetaData.JobName.ToString()).StoreDurably(true).WithDescription(jobMetaData.JobDescription).Build();
}
Exception Stack Trace for NullReferenceException
[10:43:31 ERR] Job DEFAULT.HR First Contact threw an unhandled Exception:
System.NullReferenceException: Object reference not set to an instance of an object.
at Quartz.Core.JobRunShell.Run(CancellationToken cancellationToken)
[10:43:32 ERR] Job DEFAULT.HR First Contact threw an exception.
Quartz.SchedulerException: Job threw an unhandled exception.
---> System.NullReferenceException: Object reference not set to an instance of an object.
at Quartz.Core.JobRunShell.Run(CancellationToken cancellationToken)
--- End of inner exception stack trace --- [See nested exception: System.NullReferenceException: Object reference not set to an instance of an object.
at Quartz.Core.JobRunShell.Run(CancellationToken cancellationToken)]
Edit:
I tried using the MS DI Integration library with Quartz Hosted services but couldn't get it to work with persistent Job storage & MySql.
builder.Services.Configure<QuartzOptions>(builder.Configuration.GetSection("Quartz"));
builder.Services.Configure<QuartzOptions>(options =>
{
options.Scheduling.IgnoreDuplicates = true;
options.Scheduling.OverWriteExistingData = true;
});
builder.Services.AddQuartz(q =>
{
q.SchedulerId = "Job Creator";
q.UseMicrosoftDependencyInjectionJobFactory();
var jobKey = new JobKey("HR First Contact", "DEFAULT");
q.AddJob<MailHRNewEmployee>(jobKey, j => j
.StoreDurably()
.WithDescription("job blueprint"));
q.UsePersistentStore(s =>
{
s.PerformSchemaValidation = true;
s.UseProperties = true;
s.RetryInterval = TimeSpan.FromSeconds(15);
//Neither .UseMySql nor UseMySqlConnector work
//MySqlConnector should be the one I want though
s.UseMySqlConnector(MySql =>
{
//I am loading the configuration above but don't know how to use the connection string here?
//I thought it would maybe be Configuration.GetConnectionString("xyz");
MySql.ConnectionString = "MyConnectionString";
MySql.TablePrefix = "QRTZ_";
});
s.UseJsonSerializer();
});
});
builder.Services.AddTransient<MailHRNewEmployee>();
builder.Services.AddQuartzHostedService(options =>
{
options.WaitForJobsToComplete = true;
});
Jobs should be registered as transient, Quartz expects to get a new instance from job factory. Have you considered using the MS DI integration library and hosted services integration library?
I have class to keep PowerShell session. So that I can access the powershell session without to create a new session . Below is my snippet code
public class PowerShellSession : IHttpHandler, IRequiresSessionState
{
public bool IsReusable
{
get
{
return false;
}
}
public void ProcessRequest(HttpContext context)
{
throw new NotImplementedException();
}
public PowerShell PowerShell2010()
{
if(HttpContext.Current.Session == null)
{
WSManConnectionInfoSession connExch = new WSManConnectionInfoSession();
var session = connExch.GetExchangeConnectionSession(2010);
Runspace runspace = RunspaceFactory.CreateRunspace(session);
runspace.Open();
PowerShell Shell = PowerShell.Create();
Shell.Runspace = runspace;
HttpContext.Current.Session["PowerShell2010"] = Shell;
return Shell;
}
if (HttpContext.Current.Session["PowerShell2010"] != null)
{
WSManConnectionInfoSession connExch = new WSManConnectionInfoSession();
var session = connExch.GetExchangeConnectionSession(2010);
Runspace runspace = RunspaceFactory.CreateRunspace(session);
runspace.Open();
PowerShell Shell = PowerShell.Create();
Shell.Runspace = runspace;
HttpContext.Current.Session["PowerShell2010"] = Shell;
return Shell;
}
else
{
return (PowerShell)HttpContext.Current.Session["PowerShell2010"];
}
}
}
The problem is my code always return "Object Reference not set to an instance of an object" when I try to set value to session.
Here the code to set value on session
HttpContext.Current.Session["PowerShell2010"] = Shell;
Did I do something wrong?
I have no experience using Powershell. Having said that, a big part of the problem seems to be that your if() statements are incorrect.
First you check if(HttpContext.Current.Session == null) which is TRUE if the Session object can not be found in the current context. But then you proceed by attempting to use that Session object anyway, so no wonder you get the error that you are getting.
The next one seems wrong too: if (HttpContext.Current.Session["PowerShell2010"] != null), which would be TRUE if a previous attempt to store a Powershell object was successful. But then you proceed to create and store a new Powershell object, which totally defeats the cache that you apparently wanted. You need to replace this with == null, assuming that you will find a way to get to the Session object in the first place.
And last but not least, to have a bigger chance of getting to the HTTP Session object:
make sure that Session state is enabled in your web server and/or Web.Config files;
run as much of the code above in an MVC Controller class, instead of in a Type Library class or something like that. Or pass the HTTP Session object from the MVC Controller Action-method into the Type Library method using a parameter.
I know this has been asked lot of times and I have read/tried most of the solutions I can ever find. But I can not find the exact solution to my problem. Most of them are calling the save() but I am only getting the list. This is in groovy/grails.
the error "failed to lazily initialize a collection, no session or session was closed" occurs when this code is executed the return caseVisualImpairmentCauses part. When the caseVisualImpairmentCauses contains value, it returns the error "object references an unsaved transient instance - save the transient instance before flushing".
def List<CaseVisualImpairmentCause> bindVisualImpairmentCause(Long visualImpairmentPrimaryCauseId, ArrayList caseVisualImpairmentCausesList,
String visualImpairmentOtherCause) {
def caseVisualImpairmentCauses = []
if (visualImpairmentPrimaryCauseId) {
def visualImpairmentPrimaryCauseInstance = VisualImpairmentCause.get(visualImpairmentPrimaryCauseId)
CaseVisualImpairmentCause caseVisualImpairmentPrimaryCause = new CaseVisualImpairmentCause(visualImpairmentCauseIdvisualImpairmentCause: visualImpairmentPrimaryCauseInstance)
caseVisualImpairmentPrimaryCause.isPrimary = true
caseVisualImpairmentCauses << caseVisualImpairmentPrimaryCause
}
caseVisualImpairmentCausesList.each {
VisualImpairmentCause visualImpairmentCause = VisualImpairmentCause.get(it as Integer)
CaseVisualImpairmentCause caseVisualImpairmentCause = new CaseVisualImpairmentCause(visualImpairmentCauseIdvisualImpairmentCause: visualImpairmentCause)
if (it.equals('5')) {
caseVisualImpairmentCause.caseVisualImpairmentCauseOther = visualImpairmentOtherCause
}
caseVisualImpairmentCauses.add(caseVisualImpairmentCause)
}
return caseVisualImpairmentCauses
}
the one calling that is
obpCaseInstance.visualImpairmentCauses = caseService.bindVisualImpairmentCause(visualImpairmentPrimaryCauseId, listOfCaseVisualImpairmentCauses, visualImpairmentOtherCause)
any idea why this error happens?
I was able to solve this by changing all findbyId(), .get() and getbyId() into .read(). It appears that the methods(find, get) are already flushing and as good as calling .save(). I also took out all methods fetching data (methods that cant be replaced by .read() like findBYOtherproperty) being called from another service and put it in the same method instead. This made the error go away without me forcing to save the object first before leaving the service.
I have to do two task parallelly in the backgroud worker process in the asp.net.
The following are the two task
Sent a email alert to support guys if any error event occurs in any of the server in a domain (All the time)
Sent the status report of error messages for all the servers in a domain at a time for every 4 hours or based on the user configuration.
I have already implemented the first task through the background worker process (
Now I want to implement second one and that fetch the data from the database for every 4 hours or user configured.
Please find the below are source code that i have used for implementation.
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(DoWork);
worker.WorkerReportsProgress = false;
worker.WorkerSupportsCancellation = true;
worker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(WorkerCompleted);
worker.RunWorkerAsync();
}
private static void DoWork(object sender, DoWorkEventArgs e)
{
GetEmailAlertData objGetEmailAlertData = new GetEmailAlertData();
List<Email> lstEmaildetails = objGetEmailAlertData.GetEmailAlertDetails();
SentinelAlerter objSentinelAlerter = new SentinelAlerter();
foreach (Email objEmail in lstEmaildetails)
{
try
{
// I have implement first task
objAlerter.SendAlert(objEmail);
}
catch (Exception ex)
{
}
finally
{
}
}
}
Can anyone suggest how to implement the second task in the same background worker process.
I would recommend you to use Task Scheduler, its easy and will sort your issues out.
I am having a problem with transaction in Grails. I want to save a list of object to DB by a checking condition at each object. All these process I want to put to one transaction, it means if the k-th object does not satisfied the checking condition, all previous objects (from the first object to the (k-1)th one) will be rolled back from DB. Here is my example:
static transactional = true
public void saveManyPeople() {
// ...
List<People> peoples = new ArraysList();
for(i = 0, i < n, i++) {
People newPeople = createPeopleFromRawData(); // return a people object in memory
if(<checking-condition>) {
newPeople.save(flush : false)
} else {
throw new MyCustomizedException() // MyCustomizedException has extended from RuntimException
}
}
// ...
}
As you may see, I set transactional variable to true and I've tried to use flush : true and flush : false, but it didn't work as I want. I've read this article Rolling back a transaction in a Grails Service
And the author recommended that the service method should throw a RuntimeException then the process will be rollbacked. But if I want to throw another exception, so what I have to do?
Could you please give me some suggestions on this problem?
Thank you so much!
You can throw any exception that extends from RuntimeException to rollback the transaction. Or you can use Programmatic Transactions, using withTransation, to have more control over the transaction.
Could you verify that saveManyPeople() is within a Service and not a Controller?
The static transactional = true isn't respected in a Controller. I am suspecting that this is the issue.
If you need to have transactional support with the controller, you could always use DomainClass.withTransaction. Reference Documentation
Example:
Account.withTransaction { status ->
def source = Account.get(params.from)
def dest = Account.get(params.to)
def amount = params.amount.toInteger()
if(source.active) {
source.balance -= amount
if(dest.active) {
dest.amount += amount
}
else {
status.setRollbackOnly()
}
}
}