Using the power of the job service can open up a lot of tasks within the web client. By executing out of the web context they are a perfect place to run long running processes and because they can be scheduled easily, can allow a lot of cool things to happen.
There is not a lot of documentation on exactly how to create a new job service job however which is where this post comes in. The first thing to know are the jobs are compiled assemblies. That means we are going to use Visual Studio to build this. I am not going to go into the ins and outs of using VS. What I will do is to define what you need minimally to put a job together.
Step 1
In Visual Studio, create an class library project.
Step 2
Add the required assemblies. All of these files can be found in a deployed Infro CRM client web site Bin folder. The assemblies required are:
NHibernate
Quartz
Sage.Entity.Interfaces (to work with the entity model)
Sage.Platform
Sage.Scheduling
Sage.SalesLogix.BusinessRules (if relying on that for existing methods)
Step 3
Add usings
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Globalization; using Quartz; using Sage.Entity.Interfaces; using Sage.Platform.Orm; using Sage.Platform.Scheduling; using Sage.SalesLogix.BusinessRules; using System.ComponentModel;
Step 4
Create your public class name. This should inherit from SystemJobBase if you want the job to be tagged as a system job in the web client. The signature would look like:
public class MyJobIsCool : SystemJobBase { }
The class name will be used to select it as a job available to be used within the Application Architect, so name it something meaningful.
In addition, you should add attributes to your class as follows:
[DisallowConcurrentExecution] [Description("My Description")] [DisplayName("My Name")] public class MyJobIsCool : SystemJobBase { }
These attributes are exposed by the job service and are what shows in the web client as the job name and description.
Step 5
Create the OnExecute method within your class. Only one method is required for your class. An overriden OnExecute. That signature look like this:
protected override void OnExecute() { }
Within that method there are a couple of built in outputs you can use:
The following is a completed sample file that runs a query retrieving all contacts where the last name starts with A. It then loops through the records and updates all of their phone numbers to be a specific value. (not a real valuable example but hey)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Globalization; using Quartz; using Sage.Entity.Interfaces; using Sage.Platform.Orm; using Sage.Platform.Scheduling; using Sage.SalesLogix.BusinessRules; using System.ComponentModel; namespace FX.JobService { [DisallowConcurrentExecution] [Description("My Description")] [DisplayName("My Name")] public class MyJobIsCool : SystemJobBase { private static void Log(string Message) { using (System.IO.TextWriter writer = new System.IO.StreamWriter(@"C:\Temp\Test_debug_log.txt", true)) { writer.WriteLine(string.Format("{0} - {1}", DateTime.Now, Message)); } } private static readonly string _entityDisplayName = typeof(IContact).GetDisplayName(); protected override void OnExecute() { //Set the main phase status Phase = "Running"; //Set the phase detail status PhaseDetail = "Starting"; using (var session = new SessionScopeWrapper()) { //Set the phase detail status PhaseDetail = string.Format("Composing Query {0}", _entityDisplayName); // Now we build our list of records // I chose contacts starting with A for a lastname var contacts = session.QueryOver<IContact>() .WhereRestrictionOn(c => c.LastName).IsLike("a%") .List<IContact>(); //now work with my list of contacts if (contacts != null) { //Set the main phase status Phase = "Doing the work"; //Set the phase detail status PhaseDetail = string.Format("Processing {1} {0} records", _entityDisplayName, contacts.Count); //Write to my text file Log(PhaseDetail); //Initialize a counter var counter = 0; foreach (var contact in contacts) { //Here we can do a sample update like this: contact.WorkPhone = "2128675309"; contact.Save(); // halt processing if interrupt requested by job server if (Interrupted) { PhaseDetail = "Job was stopped by a user"; Log(PhaseDetail); return; } // update job progress percentage Progress = 100M * ++counter / contacts.Count; } } else { // no records to process //Set the phase detail status PhaseDetail = string.Format("No qualifying records for {0}", _entityDisplayName); //Write to my text file Log(PhaseDetail); } } //Set the main phase status Phase = "Complete"; //Set the phase detail status PhaseDetail = "Complete"; //Write to my text file Log("We are done!"); } } }
Step 6
Compile the assembly. Now take the compiled dll file and copy it into the Saleslogix Job Service portal, under the Support Files/bin folder. If you do this in Application Architect at this point you will need to close the AA and re-open it to ensure the job is available for the next step.
Step 7
Add the job to the list of jobs available in the job service portal.
Step 8
Deploy the SLXJobService portal. It is possible you might need to restart the SLX Job Server service. Not sure on that.
You should now be able to log in to the web client, and under administration/job manager and be able to see your new job listed under the Definitions tab.