patterncsharpMinor
Parallel Job Consumer using TPL
Viewed 0 times
parallelusingconsumertpljob
Problem
I need to provide a service (either a Windows Service or an Azure Worker Role) which will handle the parallel execution of jobs. These jobs could be anything from importing data to compiling of reports, or to sending out mass notifications, etc.
Database Design:
The whole process of running these jobs is persistent.
Class Structure:
In order to create new jobs the simple
```
public class JobHandler
{
public string State
{
get
{
return jobInstance.State;
}
set
{
using (MyEntities context = new MyEntities())
{
context.JobInstances.Attach(jobInstance);
jobInstance.State = value;
context.SaveChanges();
}
SaveJobEvent(String.Empty);
}
}
private JobInstance jobInstance;
public JobHandler(JobInstance job)
{
jobInstance = job;
}
public IJob CreateJobObject()
{
Type type = Type.GetType(jobInstance.JobDefinition.FullAssemblyType);
XmlSerializer serializer = new XmlSerializer(type);
using (StringReader reader = new StringReader(jobInstance.SerialisedObject))
{
return serializer.Deserialize(reader) as IJob;
}
}
public void SaveJobEvent(string jobEventMessage)
{
using (MyEntities context = new MyEntities())
{
context.JobInstances.Attach(jobInstance);
jobInstance.JobInstanceEvents.Add(
n
Database Design:
The whole process of running these jobs is persistent.
JobDefinitionstores varchar reference to theIJobconcrete type class.
JobInstanceis an instance ofJobDefinitionwhich needs to be executed as a job.
JobInstanceEventstores the process of executing an instance (the change of states).
Class Structure:
In order to create new jobs the simple
IJob interface needs to be implemented:public interface IJob
{
bool Execute();
}JobHandler is responsible for deserializing the serialized varchar in the database and also saving state changes:```
public class JobHandler
{
public string State
{
get
{
return jobInstance.State;
}
set
{
using (MyEntities context = new MyEntities())
{
context.JobInstances.Attach(jobInstance);
jobInstance.State = value;
context.SaveChanges();
}
SaveJobEvent(String.Empty);
}
}
private JobInstance jobInstance;
public JobHandler(JobInstance job)
{
jobInstance = job;
}
public IJob CreateJobObject()
{
Type type = Type.GetType(jobInstance.JobDefinition.FullAssemblyType);
XmlSerializer serializer = new XmlSerializer(type);
using (StringReader reader = new StringReader(jobInstance.SerialisedObject))
{
return serializer.Deserialize(reader) as IJob;
}
}
public void SaveJobEvent(string jobEventMessage)
{
using (MyEntities context = new MyEntities())
{
context.JobInstances.Attach(jobInstance);
jobInstance.JobInstanceEvents.Add(
n
Solution
Only some few minor things:
- In
StartPopulatingQueueto me as the reader it's unclear in what unitpollingDelayis in. Two classic solutions:
- Change it to be a
TimeSpan- I usually prefer this since it provides the most flexibility.
- Add the unit suffix to the parameter name. like
Msfor milliseconds orSecfor seconds etc.
CreateJobObjectcan potentially returnnullif the deserialized object cannot be cast toIJobin which caseExecuteJobwill throw aNullReferenceExceptionwhich is typically not very meaningful. You should throw a more meaningful exception on deserialization (as mentioned in the comment by Roman you could use a direct cast instead ofaswhich would throw anInvalidCastException)
- It feels wrong to me that the consumer defines the job execution states. The consumer seems largely responsible for managing the job queue and it should stick to that single responsibility. Hence I think
Executeshould be moved intoJobHandler.
- I'd also consider moving
CreateJobObjecttoJobInstancesoJobInstanceis responsible to provide the actual object instance. ThenJobHandleris just concerned with the state changes of the job.
Context
StackExchange Code Review Q#32073, answer score: 3
Revisions (0)
No revisions yet.