CRM 2011, Stopping custom workflow programmatically - workflow

i've been trying to stop a workflow programmatically.
I've read both in various posts and in the msdn that this can be done by updating
the Asyncoperation status via update request.
However everytime i update the request. the workflow get stuck on a mid stage such as cancelling or pausing and dosen't reach a final state.
any ideas?
protected void ExecutePostAccountUpdate(LocalPluginContext localContext)
{
if (localContext == null)
{
throw new ArgumentNullException("localContext");
}
string fetchXML = "<fetch mapping='logical' count='50' version='1.0'>" +
"<entity name='asyncoperation'>" +
"<filter>" +
"<condition attribute='regardingobjectid' operator='eq' value='" +
localContext.PluginExecutionContext.PrimaryEntityId + "' />" +
"</filter>" +
"</entity>" +
"</fetch>";
EntityCollection col = localContext.OrganizationService.RetrieveMultiple(new FetchExpression(fetchXML));
if (col.Entities.Count > 0)
{
AsyncOperation a = (AsyncOperation)col[0];
a.StateCode = AsyncOperationState.Completed;
a.StatusCode = new OptionSetValue(32);
localContext.OrganizationService.Update(a);
}
}

Have a look at my blog: How to Cancel Workflow Programmatically using C#
Make sure the user have permissions to Cancel System Jobs.

QueryExpression queryExpression = new QueryExpression("asyncoperation") { ColumnSet = new ColumnSet("statuscode") };
queryExpression.Criteria.AddCondition("name", ConditionOperator.Equal, Name of Workflow);
queryExpression.Criteria.AddCondition("regardingobjectid", ConditionOperator.Equal, regardingobjectId);
var asyncOperations = organizationService.RetrieveMultiple(queryExpression);
foreach (var asyncOperation in asyncOperations.Entities)
{
if (((OptionSetValue)asyncOperation["statuscode"]).Value == 10 || // Waiting
((OptionSetValue)asyncOperation["statuscode"]).Value == 20 || // In Process
((OptionSetValue)asyncOperation["statuscode"]).Value == 0)
{
Entity operation = new Entity("asyncoperation")
{
Id = asyncOperation.Id,
["statecode"] = new OptionSetValue(3),
["statuscode"] = new OptionSetValue(32)
};
organizationService.Update(operation);
}
}
Make sure the user have permissions to Cancel System Jobs.

It seems you can un-publish a workflow via code, according to this post.
NOTE: This does not necessarily halt an in-progress workflow, but it will prevent any new workflows of that type from being started.
const int WorkflowStatusDraft = 1;
const int WorkflowStatusPublished = 2;
public void PublishWorkflow(Guid workflowId)
{
SetStateWorkflowRequest publishRequest = new SetStateWorkflowRequest();
publishRequest.EntityId = workflowId;
publishRequest.WorkflowState = WorkflowState.Published;
publishRequest.WorkflowStatus = WorkflowStatusPublished;
this.CrmService.Execute(publishRequest);
}
public void UnpublishWorkflow(Guid workflowId)
{
SetStateWorkflowRequest unpublishRequest = new SetStateWorkflowRequest();
unpublishRequest.EntityId = workflowId;
unpublishRequest.WorkflowState = WorkflowState.Draft;
unpublishRequest.WorkflowStatus = WorkflowStatusDraft;
this.CrmService.Execute(unpublishRequest);
}

Related

How to read event source name from EventLogRecord?

I am reading event log records as -
EventLogQuery query = new EventLogQuery(_eventLogName, PathType.LogName, sQuery);
query.ReverseDirection = true; // this tells it to start with newest first
List<EventRecord> eventRecords = new List<EventRecord>();
using (EventLogReader reader = new EventLogReader(query))
{
reader.BatchSize = 100;
EventRecord eventRecord;
while ((eventRecord = reader.ReadEvent()) != null)
{
if (_lastModified == null)
{
_lastModified = eventRecord.TimeCreated;
}
eventRecords.Add(eventRecord);
}
}
I am not sure how may i get Event Source value for each event.
Any help please...

Hangfire , EF context used in the Task is not updated, the query inside the task always gives the old values

The context used inside the Hangfire task , always gives the old database values, it seems like the context is not updating inside the task. How to get the updated info context data inside hangfire task.
calling the task
BLL.Extraction NewExtractionCls = new BLL.Extraction();
var jobId = BackgroundJob.Enqueue(() => NewExtractionCls.SearchEngineExtraction("SearchURL", "1","1", null));
This is implementation
[Authorize]
public void SearchEngineExtraction(string SearchURL, int PageLimit, int SearchEngineID, PerformContext context)
{
WebClient wc = new WebClient();
#region Main Table - SearchEngineTbl
var NewExtraction = db.SearchEngineTbls.Where(x => x.SearchEngineID == SearchEngineID).FirstOrDefault();
var JobID = context.BackgroundJob.Id;
NewExtraction.JobID = Convert.ToInt32(JobID);
NewExtraction.SeachEngineURL = SearchURL;
NewExtraction.Status = "Processing";
db.SaveChanges();
var LinkCollectionRefined = ExtractLinkFromThisPage(i, SearchURL, wc).Distinct().ToList();//.Skip(10);
foreach (var Link in LinkCollectionRefined)
{
using (Entities dbRefreshed = new Entities())
{
// I get the same old value here, even if I update the table manually, when I rerun, everything is fine.
var CurrentStatusOfExtraction = db.SearchEngineTbls.Where(x => x.SearchEngineID == NewExtraction.SearchEngineID).FirstOrDefault();
if (CurrentStatusOfExtraction.IsActive == false)
{
return;
}
}
}
#endregion
}

Entity framework tracking wrong model object after helper method

Below is some basic code that's a part of our Create controller method. If a certain checkbox is selected, we want to create a copy of the agenttransmission object we are currently creating, with a couple fields altered.
When the program goes into the helper method, it creates the sub record without incident, however for some reason, when the program finishes and comes back to the Create method, the model object agenttransmission becomes the sub model object. All value, including the PK are suddenly pressent in the agenttransmission object.
Not sure how this is happening since there is a string return value on the helper method and no fields are touched on the agenttransmission record.
Create method
//Create substat if requested
if (agenttransmission.OverrideId)
{
status += ". " + CreateSubStat(agenttransmission);
}
Helper method
public string CreateSubStat(AgentTransmission master)
{
string msg = string.Empty;
if (ModelState.IsValid)
{
using (AgentResourcesEntities tempDb = new AgentResourcesEntities())
{
try
{
//Check to see if substat already exists
var check = (from a in db.AgentTransmission
where a.ParentId == master.ID && a.RecordStatus.Equals("P")
select a).ToList();
if (check.Count > 0) return string.Empty;
//If not add dependent record
AgentTransmission sub = master;
sub.OverrideId = false;
sub.DRMCompanyId = string.Empty;
sub.CONCode = "00";
sub.RecordStatus = "P";
sub.ParentId = master.ID;
sub.ID = 0;
sub.IsSubstat = true;
sub.SendToDynamicsOptions = "N";
sub.SendToNMF = false;
//Remove blanks from ClearinghousePartners list
sub.ClearinghousePartners.RemoveAll(
x =>
string.IsNullOrWhiteSpace(x.ClearingHouseName) &&
string.IsNullOrWhiteSpace(x.TradingPartnerName) && x.StartDate == null);
sub.AgentRelationshipCodes.RemoveAll(
x =>
string.IsNullOrWhiteSpace(x.RelationshipId) &&
!x.EffectiveDate.HasValue && x.Id == 0);
foreach (var item in sub.AgentRelationshipCodes)
{
item.LastChangeDate = DateTime.Now;
item.LastChangeId = SecurityRules.GetUserName(User);
item.AgentTransmission = sub;
item.AgtTableId = sub.ID;
}
foreach (var item in sub.ClearinghousePartners)
{
item.AgentTransmission = sub;
item.AgtTransId = sub.ID;
}
db.AgentTransmission.Add(sub);
db.SaveChanges();
msg = "Substat saved with status of 'Dependent'.";
}
catch (DbEntityValidationException dbEx)
{
msg = "Error creating substat. IT has been informed and will respond shortly.";
SendEmail.ErrorMail(dbEx.Message, SecurityRules.GetUserName(User));
}
catch (Exception ex)
{
msg = "Error creating substat. IT has been informed and will respond shortly.";
SendEmail.ErrorMail(ex, SecurityRules.GetUserName(User));
}
}
}
else
{
//Invalid ModelState error handling
string messages = string.Join("; ", ModelState.Values
.SelectMany(x => x.Errors)
.Select(x => x.ErrorMessage));
SendEmail.ErrorMail("Error Creating Substat: " + messages, SecurityRules.GetUserName(User));
msg = "Error creating substat. IT has been informed and will respond shortly.";
}
return msg;
}
The line...
AgentTransmission sub = master;
...doesn't copy master to sub, but assigns it to sub, because AgentTransmission is a reference type. So everything you do to sub you do to master.
You either must clone master to sub (create a new object and copy its properties), or re-fetch the master object from the database after the CreateSubStat call.

getNextFireTime of my existing job

I tried Quartz.com documentation & googled for couple for hours...but could not find single good article on how to get Next Job (which is supposr to fire in future).
I am using CronTrigger Expression to Schedule jobs, using VB.net (winforms). jobs works fine...however I would like my users to see when will it FIRE next. I am storing CronExpression in my database, Can I use that Expression to show next Fire Date/Time to my end users? or if there is any other way possible please advise (with a simple samply).
Thanks
Edit
Following Code Returns next job will fire after 12 minutes instead of 20 minute
Dim exp As CronExpression = New CronExpression("0 0/20 * * * ?")
Dim nextFire As String = exp.GetNextValidTimeAfter(DateTime.Now)
MsgBox(nextFire)
You can create a new JobKey
JobKey jobKey = new JobKey(jobName, groupName);
and use the key to fetch the job detail:
var detail = scheduler.GetJobDetail(jobKey);
this is a simple function which does what you're looking for:
private DateTime getNextFireTimeForJob(IScheduler scheduler, string jobName, string groupName = "")
{
JobKey jobKey = new JobKey(jobName, groupName);
DateTime nextFireTime = DateTime.MinValue;
bool isJobExisting = Scheduler.CheckExists(jobKey);
if (isJobExisting)
{
var detail = scheduler.GetJobDetail(jobKey);
var triggers = scheduler.GetTriggersOfJob(jobKey);
if (triggers.Count > 0)
{
var nextFireTimeUtc = triggers[0].GetNextFireTimeUtc();
nextFireTime = TimeZone.CurrentTimeZone.ToLocalTime(nextFireTimeUtc.Value.DateTime);
}
}
return (nextFireTime);
}
It works only if you have one trigger per job.
If there's more than one trigger in your job you can loop through them:
foreach (ITrigger trigger in triggers)
{
Console.WriteLine(jobKey.Name);
Console.WriteLine(detail.Description);
Console.WriteLine(trigger.Key.Name);
Console.WriteLine(trigger.Key.Group);
Console.WriteLine(trigger.GetType().Name);
Console.WriteLine(scheduler.GetTriggerState(trigger.Key));
DateTimeOffset? nextFireTime = trigger.GetNextFireTimeUtc();
if (nextFireTime.HasValue)
{
Console.WriteLine(TimeZone.CurrentTimeZone.ToLocalTime(nextFireTime.Value.DateTime).ToString());
}
}
or using Linq (System.Linq) :
var myTrigger = triggers.Where(f => f.Key.Name == "[trigger name]").SingleOrDefault();
If you already know the cronExpression then you can call GetNextValidTimeAfter , eg
CronExpression exp = new CronExpression("0 0 0/1 1/1 * ? *");
var nextFire = exp.GetNextValidTimeAfter(DateTime.Now);
Console.WriteLine(nextFire);
and if you want more fire times, then
for(int i=0 ; i< 9; i++)
{
if (nextFire.HasValue)
{
nextFire = exp.GetNextValidTimeAfter(nextFire.Value);
Console.WriteLine(nextFire);
}
}
If you are looking for a more general way of showing next fire times for existing jobs, then check out the answer to this question which works even if you aren't using cron expressions. This is for Quartz Version 2
The Quartz Version 1 way of getting job and trigger information is something like the method below.
public void GetListOfJobs(IScheduler scheduler)
{
var query =
(from groupName in scheduler.JobGroupNames
from jobName in scheduler.GetJobNames(groupName)
let triggers = scheduler.GetTriggersOfJob(jobName, groupName)
select new
{
groupName,
jobName,
triggerInfo = (from trigger in triggers select trigger)
}
);
foreach (var r in query)
{
if (r.triggerInfo.Count() == 0)
{
var details = String.Format("No triggers found for : Group {0} Job {1}", r.groupName, r.jobName);
Console.WriteLine(details);
}
foreach (var t in r.triggerInfo)
{
var details = String.Format("{0,-50} {9} Next Due {1,30:r} Last Run {2,30:r} Group {3,-30}, Trigger {4,-50} {5,-50} Scheduled from {6:r} Until {7,30:r} {8,30:r} ",
t.JobName,
t.GetNextFireTimeUtc().ToLocalTime(),
t.GetPreviousFireTimeUtc().ToLocalTime(),
t.JobGroup,
t.Name,
t.Description,
t.StartTimeUtc.ToLocalTime(),
t.EndTimeUtc.ToLocalTime(),
t.FinalFireTimeUtc.ToLocalTime(),
((t.GetNextFireTimeUtc() > DateTime.UtcNow) ? "Active " : "InActive")
);
Console.WriteLine(details);
}
}
}
I had problem where i am getting next execution time even for the date which are past the end time.
Following is my implementation which eventually returning the correct result in Quartz 3.0.4
internal static DateTime? GetNextFireTimeForJob(IJobExecutionContext context)
{
JobKey jobKey = context.JobDetail.Key;
DateTime? nextFireTime = null;
var isJobExisting = MyQuartzScheduler.CheckExists(jobKey);
if (isJobExisting.Result)
{
var triggers = MyQuartzScheduler.GetTriggersOfJob(jobKey);
if (triggers.Result.Count > 0)
{
var nextFireTimeUtc = triggers.Result.First().GetNextFireTimeUtc();
if (nextFireTimeUtc.HasValue)
{
nextFireTime = TimeZone.CurrentTimeZone.ToLocalTime(nextFireTimeUtc.Value.DateTime);
}
}
}
return nextFireTime;
}

Submitting a WebRequest from within a CLR Trigger

I just implemented a prototype solution for updating my caching server in real-time by assigning a CLR Trigger to a table so that whenever a certain column is updated the URL called from the trigger will update the caching server with the correct data.
It's working fine and the code is as follows:
[Microsoft.SqlServer.Server.SqlTrigger(Name = "AdStatusChanged", Target = "Ads", Event = "FOR UPDATE")]
public static void AdStatusChanged()
{
SqlTriggerContext triggContext = SqlContext.TriggerContext;
int adID = 0, adStatusID_Old = 0, adStatusID_New = 0;
if (triggContext.TriggerAction == TriggerAction.Update)
{
using (SqlConnection conn = new SqlConnection("context connection=true"))
{
conn.Open();
SqlCommand sqlComm = new SqlCommand();
SqlPipe sqlP = SqlContext.Pipe;
sqlComm.Connection = conn;
sqlComm.CommandText = "SELECT AdID, AdStatusID from INSERTED";
SqlDataReader reader = sqlComm.ExecuteReader();
if (reader.Read())
{
adID = reader.GetInt32(0);
adStatusID_New = reader.GetInt32(1);
}
reader.Close();
sqlComm.CommandText = "SELECT AdID, AdStatusID from DELETED WHERE AdID = " + adID;
reader = sqlComm.ExecuteReader();
if (reader.Read())
{
adID = reader.GetInt32(0);
adStatusID_Old = reader.GetInt32(1);
}
}
if (adID == 0 || adStatusID_New == adStatusID_Old)
{
// Check could be more thorough !
return;
}
WebResponse httpResponse = null;
try
{
string apiURL = string.Format("{0}/{1}", "http://localhost:14003/Home", "UpdateAdStatus?adID=" + adID + "&adStatusID=" + adStatusID_New);
var httpWebRequest = (HttpWebRequest)WebRequest.Create(apiURL);
httpWebRequest.Method = "GET";
httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
// check for successful response
}
catch (Exception ex)
{
Log("WebRequest from within SQL Server failed ! " + ex.Message);
}
finally
{
if (httpResponse != null)
{
httpResponse.Close();
}
}
}
}
I would like to have some expert/experienced views on the "CONS" of this approach regarding performance, deadlocks, sql crashing, or other areas that could be of potential concern.
Has anyone tried this (I'm sure many must have) and what was the result ? a successful implementation or did you revert to some other method or updating the cache real-time?