I have a custom workflow activity which accepts an input parameter called "InputTest". This is just a string.
[Input("InputTest")]
[RequiredArgument]
public InArgument<string> TargetEntity { get; set; }
In my custom workflow activity I want to read it, so I do the following:
string targetEntityValue = TargetEntity.Get(executionContext);
As soon as I add that line my workflow activity will no longer execute. When I run it the status will show "Succeeded" but nothing in the workflow will have run, not even the trace at the beginning of the workflow to say the workflow has been entered. There is nothing in the Diag Logs. When I run the SQL Profiler there are only a few statements added to the AsyncBase table showing that the workflow has run and instantly finished.
If I remove the above line the workflow runs ok. I am wondering what I am doing wrong here? Why would reading an input parameter cause CRM not to do anything in the workflow?
It is almost like somehow the code isnt entering its main method.
[Input("Entity")]
[RequiredArgument]
public InArgument<string> TargetEntity { get; set; }
protected override void Execute(CodeActivityContext executionContext)
{
// Create the tracing service
ITracingService tracingService = executionContext.GetExtension<ITracingService>();
if (tracingService == null)
{
throw new InvalidPluginExecutionException("Failed to retrieve tracing service.");
}
tracingService.Trace("Entered TestWorkflow.Execute(), Activity Instance Id: {0}, Workflow Instance Id: {1}",
executionContext.ActivityInstanceId,
executionContext.WorkflowInstanceId);
string targetEntityValue = TargetEntity.Get<string>(executionContext);
}
Ok well this was very strange. In the end I got desperate so I created a new project with a new assembly name, added the code back in and deployed and now it is working fine.
So.. I think either:
This is my first custom workflow plugin so perhaps I set up the original project wrong somehow and for some reason it was not entering its main method.
There is some kind of issue with the deployment of the original plugin on the CRM server.
Quite why CRM would run a plugin and return status of "Success" without running the main method I am not sure.
if you want to read a Custom Workflow Activity parameter, the right syntax is:
string targetEntityValue = TargetEntity.Get<string>(executionContext);
Related
I've created a web job in Azure. This web job gets triggered by my web api. This web job also is set to run "continuously". However, from the moment I start the web job, I receive this error message (seen in the logs):
Can't bind Queue to type 'System.String'
And my web job tries to restart, and gets the same error message again, and again.
I've tried to do some research on this error message, but I'm not finding much of anything. Has anyone ever seen this error before? Here is my code in the functions.cs file:
namespace DownloadProcessor
{
public class Functions
{
public static void ProcessQueueMessage([Queue("ringclonedownloadprocessorqueue")] string message, TextWriter log)
{
log.WriteLine("Processing" + message);
}
}
}
It seems very straightforward. Also, here is my function in my Program.cs file:
namespace DownloadProcessor
{
// To learn more about Microsoft Azure WebJobs SDK, please see http://go.microsoft.com/fwlink/?LinkID=320976
class Program
{
// Please set the following connection strings in app.config for this WebJob to run:
// AzureWebJobsDashboard and AzureWebJobsStorage
static void Main()
{
var host = new JobHost();
// The following code ensures that the WebJob will be running continuously
host.RunAndBlock();
}
}
}
It all seems very straightforward, and I have created other similar web jobs exactly like this. Has anyone ever seen this error message before? Any idea what I might be doing wrong?
I got this error when using functions in Node.js environment. In my case, it was due to the fact I forgot to specify the "direction": "out" for the output binding in function.json file
Just in case someone runs into this issue, I found out what was going on (in my case). My problem was with this line:
public static void ProcessQueueMessage([Queue("ringclonedownloadprocessorqueue")] string message, TextWriter log)
This web job was not a continuous job when I first created it. And when Visual Studio created the code template, it used [Queue("ringclonedownloadprocessorqueue")] string message as the default parameter type. I recently changed the job to a continuous job, and a continuous job uses a slightly different param type (QueueTrigger instead of Queue). But when I changed the job to continuous, Visual Studio did not change the param type accordingly. I needed to go in and manually update the param type. Changing the function to this did the trick:
namespace DownloadProcessor
{
public class Functions
{
public static void ProcessQueueMessage([QueueTrigger("ringclonedownloadprocessorqueue")] string message, TextWriter log)
{
log.WriteLine("Processing" + message);
}
}
}
I am trying to call Dynamics CRM workflow from my C# code. My workflow expects parameters from my C# code. I am using flowing code:
var workflowInfo = result.Entities.FirstOrDefault();
if (workflowInfo != null)
{
InputArgumentCollection inputParameters = new InputArgumentCollection();
EntityReference reference = new EntityReference();
reference.Id = paymentRunID;
reference.Name = "paymentrunid";
reference.LogicalName = "paymentrun";
inputParameters.Add("InputPaymentRunID", reference);
inputParameters.Add("InputPaymentDueDate", paymentRunPayDate);
OptionSetValue opt = new OptionSetValue();
opt.Value = region;
inputParameters.Add("InputRegion", opt);
var request = new ExecuteWorkflowRequest();
request.WorkflowId = workflowInfo.GetAttributeValue<Guid>("workflowid"); //ID of the workflow to execute
request.EntityId = paymentRunID; //ID of the record on which the workflow executes
request.InputArguments = inputParameters;
ServerConnection.CrmService.Execute(request);
return true;
}
Code at my CRM workflow side is:
[RequiredArgument]
[Input("EntityReference input")]
[ReferenceTarget("paymentrun")]
public InArgument<EntityReference> InputPaymentRunID { get; set; }
[Input("DateTime input")]
public InArgument<DateTime> InputPaymentDueDate { get; set; }
[Input("OptionSetValue input")]
[Default("1")]
[AttributeTarget("paymentrun", "lregion")]
public InArgument<OptionSetValue> InputRegion { get; set; }
When I run my C# code, it's executed successfully and returns true but at my Dynamics CRM workflow side I am getting following error:
This workflow cannot run because arguments provided by parent workflow does not match with the specified parameters in linked child workflow. Check the child workflow reference in parent workflow and try running this workflow again.
Unhandled Exception: Microsoft.Crm.CrmException: This workflow cannot run because arguments provided by parent workflow does not match with the specified parameters in linked child workflow. Check the child workflow reference in parent workflow and try running this workflow again.
at Microsoft.Crm.Workflow.Services.InputArgumentValidator.VerifyAndFilterInputParametersSupplied(Dictionary`2 inputArguments, Dictionary`2 childParameters)
at Microsoft.Crm.Workflow.ActivityHostBase.FetchInputArguments(ICommonWorkflowContext context)
at Microsoft.Crm.Workflow.ActivityHost.StartWorkflowExecution(Activity workflow, ICommonWorkflowContext context)
at Microsoft.Crm.Workflow.ActivityHostBase.StartWorkflow(ICommonWorkflowContext context, Activity preLoadedActivity)
What is wrong in C# code or workflow code.
I'm going to assume you are not using a XAML workflow here.
I think you have confused a workflow with a custom workflow activity (CWA), and even then I think your design is probably wrong.
A workflow is a process configured via the CRM user interface using the workflow designer. A workflow contains a number of steps. Workflows can be triggered by system events, and the ExecuteWorkflowRequest call.
A CWA is a piece of code you can package and place inside a process as a step. The CWA can only be triggered via a process. You cannot use the ExecuteWorkflowRequest call to direct access a CWA. You need to design a process and then add your CWA into it as a step, passing the inputs via the workflow designer.
Your implementation suggests you want to create a endpoint you can pass arguments to and then run some custom code. In this case a workflow won't work in any case - it cannot recieve inputs (which I suspect is the cause of your error). You should consider an action (another type of process) which allows input arguments to be defined. The action is also implemented using the workflow designer, so you can call your CWA and then pass through the arguments from the action.
I am new at messaging architectures, so I might be going at this the wrong way. But I wanted to introduce NServiceBus slowly in my team by solving a tiny problem.
Appointments in Agenda's have states. Two users could be looking at the same appointment in the same agenda, in the same application. They start this application via a Remote session on a central server. So if user 1 updates the state of the appointment, I'd like user 2 to see the new state 'real time'.
To simulate this or make a proof of concept if you will, I made a new console application. Via NuGet I got both NServiceBus and NServiceBus.Host, because as I understood from the documentation I need both. And I know in production code it is not recommended to put everything in the same assembly, but the publisher and subscriber will most likely end up in the same assembly though...
In class Program method Main I wrote the following code:
BusConfiguration configuration = new BusConfiguration();
configuration.UsePersistence<InMemoryPersistence>();
configuration.UseSerialization<XmlSerializer>();
configuration.UseTransport<MsmqTransport>();
configuration.TimeToWaitBeforeTriggeringCriticalErrorOnTimeoutOutages(new TimeSpan(1, 0, 0));
ConventionsBuilder conventions = configuration.Conventions();
conventions.DefiningEventsAs(t => t.Namespace != null
&& t.Namespace.Contains("Events"));
using (IStartableBus bus = Bus.Create(configuration))
{
bus.Start();
Console.WriteLine("Press key");
Console.ReadKey();
bus.Publish<Events.AppointmentStateChanged>(a =>
{
a.AppointmentID = 1;
a.NewState = "New state";
});
Console.WriteLine("Event published.");
Console.ReadKey();
}
In class EndPointConfig method Customize I added:
configuration.UsePersistence<InMemoryPersistence>();
configuration.UseSerialization<XmlSerializer>();
configuration.UseTransport<MsmqTransport>();
ConventionsBuilder conventions = configuration.Conventions();
conventions.DefiningEventsAs(t => t.Namespace != null
&& t.Namespace.Contains("Events"));
AppointmentStateChanged is a simple class in the Events folder like so:
public class AppointmentStateChanged: IEvent {
public int AppointmentID { get; set; }
public string NewState { get; set; }
}
AppointmentStateChangedHandler is the event handler:
public class AppointmentStateChangedHandler : IHandleMessages<Events.AppointmentStateChanged> {
public void Handle(Events.AppointmentStateChanged message) {
Console.WriteLine("AppointmentID: {0}, changed to state: {1}",
message.AppointmentID,
message.NewState);
}
}
If I start up one console app everything works fine. I see the handler handle the event. But if I try to start up a second console app it crashes with: System.Messaging.MessageQueueException (Timeout for the requested operation has expired). So i must be doing something wrong and makes me second guess that I don't understand something on a higher level. Could anyone point me in the right direction please?
Update
Everthing is in the namespace AgendaUpdates, except for the event class which is in the AgendaUpdates.Events namespace.
Update 2
Steps taken:
Copied AgendaUpdates solution (to AgendaUpdates2 folder)
In the copy I changed MessageEndpointMappings in App.Config the EndPoint attribute to "AgendaUpdates2"
I got MSMQ exception: "the queue does not exist or you do not have sufficient permissions to perform the operation"
In the copy I added this line of code to EndPointConfig: configuration.EndpointName("AgendaUpdates2");
I got MSMQ exception: "the queue does not exist or you do not have sufficient permissions to perform the operation"
In the copy I added this line of code to the Main methodin the Program class:
configuration.EndpointName("AgendaUpdates2");
Got original exception again after pressing key.
--> I tested it by starting 2 visual studio's with the original and the copied solution. And then start both console apps in the IDE.
I'm not exactly sure why you are getting that specific exception, but I can explain why what you are trying to do fails. The problem is not having publisher and subscriber in the same application (this is possible and can be useful); the problem is that you are running two instances of the same application on the same machine.
NServiceBus relies on queuing technology (MSMQ in your case), and for everything to work properly each application needs to have its own unique queue. When you fire up two identical instances, both are trying to share the same queue.
There are a few things you can tinker with to get your scenario to work and to better understand how the queuing works:
Change the EndPointName of your second instance
Run the second instance on a separate machine
Separate the publisher and subscriber into separate processes
Regardless of which way you go, you will need to adjust your MessageEndpointMappings (on the consumer/subscriber) to reflect where the host/publisher queue lives (the "owner" of the message type):
http://docs.particular.net/nservicebus/messaging/message-owner#configuring-endpoint-mapping
Edit based on your updates
I know this is a test setup/proof of concept, but it's still useful to think of these two deployments (of the same code) as publisher/host and subscriber/client. So let's call the original the host and the copy the client. I assume you don't want to have each subscribe to the other (at least for this basic test).
Also, make sure you are running both IDEs as Administrator on your machine. I'm not sure if this is interfering or not.
In the copy I changed MessageEndpointMappings in App.Config the EndPoint attribute to "AgendaUpdates2" I got MSMQ exception: "the queue does not exist or you do not have sufficient permissions to perform the operation"
Since the copy is the client, you want to point its mapping to the host. So this should be "AgendaUpdates" (omit the "2").
In the copy I added this line of code to EndPointConfig: configuration.EndpointName("AgendaUpdates2"); I got MSMQ exception: "the queue does not exist or you do not have sufficient permissions to perform the operation"
In the copy I added this line of code to the Main methodin the Program class: configuration.EndpointName("AgendaUpdates2"); Got original exception again after pressing key
I did not originally notice this, but you don't need to configure the endpoint twice. I believe your EndPointConfig is not getting called, as it is only used when hosting via the NSB host executable. You can likely just delete this class.
This otherwise sounds reasonable, but remember that your copy should not be publishing if its the subscriber, so don't press any keys after it starts (only press keys in the original).
If you want to publisher to also be the receiver of the message, you want to specify this in configuration.
This is clearly explained in this article, where the solution to your problem is completely at the end of the article.
I'm new to Windows Workflow and am using 4.5 to create a long-running workflow. I did a lot of online search, trying to find a way to create a Bookmark and ResumeBookmark without user input. The info I've read so far all requires a Console.ReadLine (user input) in order to resume a Bookmark. Is Bookmark only used for human input? I'm using the Delay Activity for now, but would like to use Bookmark.
My Workflow.xaml is like this:
Send email to reviewers, who are asked to complete their respective task. The
email is just a notification. There is no approve or reject button.
Delay Activity. This is to make the workflow persist in the persistence
database.
Check another database to see if some data are updated by reviewers.
Delay Activity again, if reviewers have not updated the data.
Send email to approver. if the data are updated. Approver's response will be recorded in the database. The email is just a notification.
Delay Activity again, waiting for approver's updating response in database.
and so on.
I'd really appreciate your help.
Bookmarks do not require user input.
You create a bookmark inside an activity:
context.CreateBookmark("bookmarkName", new BookmarkCallback(OnResumeBookmark));
Where "OnResumeBookmark" is a method in your activity.
Then when you resume the workflow you use this:
WorkflowApplication wfApp= new WorkflowApplication(new NameOFWorkflow());
wfApp.Run();
wfApp.ResumeBookmark("bookmarkName");
OnResumeBookmark will then execute.
Here is a fuller version http://msdn.microsoft.com/en-us/library/ee191721(v=vs.110).aspx
The stuff in there about console.read is just a way to show you how the bookmark name can be a variable rather than a string:
context.CreateBookmark(BookmarkName.Get(context), <-- get name from the InArgumen
Here a sample code of a custom activity:
public sealed class WaitForResponse<TResult> : NativeActivity<TResult>
{
public string ResponseName { get; set; }
protected override bool CanInduceIdle
{
get
{
return true;
}
}
protected override void Execute(NativeActivityContext context)
{
context.CreateBookmark(this.ResponseName, new BookmarkCallback(this.ReceivedResponse));
// Put code here...
}
void ReceivedResponse(NativeActivityContext context, Bookmark bookmark, object obj)
{
this.Result.Set(context, (TResult)obj);
}
This activity will run the method Execute and wait (persist/unload) until a ResumeBookmark. The ResumeBookmark can be a WCF call or a invoke of WorkflowApplication.ResumeBookmark.
I have developed a 32 bit service, I am running it in Windows 7 Home Premium x64.
Problem is when I start it, windows gives me the following message
The WLConsumer service on Local Computer started and then stopped. Some services stop
automatically if they are not in use by other services or programs.
I found the following message in the event log
Service cannot be started. System.ArgumentException: Log WLConsumer has already been registered as a source on the local computer.
at System.Diagnostics.EventLogInternal.CreateEventSource(EventSourceCreationData sourceData)
at System.Diagnostics.EventLogInternal.VerifyAndCreateSource(String sourceName, String currentMachineName)
at System.Diagnostics.EventLogInternal.WriteEntry(String message, EventLogEntryType type, Int32 eventID, Int16 category, Byte[] rawData)
at System.Diagnostics.EventLog.WriteEntry(String message, EventLogEntryType type)
at WeblogicConsumerService.WeblogicConsumer.winEventlogMe(String logTxt, String logSrc, Char entryType) in C:\Program Files (x86)\CSI\WeblogicConsumerService\WeblogicConsumer.cs:line 136
at WeblogicConsumerService.WeblogicConsumer.OnStart(String[] args) in C:\Program Files (x86)\CSI\WeblogicConsumerService\WeblogicConsumer.cs:line 63
at System.ServiceProcess.ServiceBase.ServiceQueuedMainCallback(Object state)
This is my codeblock in the OnStart() method
protected override void OnStart(string[] args)
{
#region WEBLOGIC CREDENTIALS
try
{
//Weblogic URL
this.url = Registry.LocalMachine.OpenSubKey(#"Software\CSI_WL").GetValue("URL").ToString();
//Queue name
this.qName = Registry.LocalMachine.OpenSubKey(#"Software\CSI_WL").GetValue("Queue").ToString();
//Weblogic login name
this.user = Registry.LocalMachine.OpenSubKey(#"Software\CSI_WL").GetValue("User").ToString();
//Weblogic password
this.pwd = Registry.LocalMachine.OpenSubKey(#"Software\CSI_WL").GetValue("Pwd").ToString();
//Weblogic Connection Factory
this.cfName = Registry.LocalMachine.OpenSubKey(#"Software\CSI_WL").GetValue("ConnectionFactory").ToString();
//root folder
this.rFolder = Registry.LocalMachine.OpenSubKey(#"Software\CSI_WL").GetValue("root").ToString();
}
catch (Exception e)
{
winEventlogMe(e.Message, "WLRegistryKeys", 'e');
}
#endregion
winEventlogMe("Successful start", "SeriviceStartup", 'i');
synchro.Enabled = true;
}
winEventLogMe is the method I am calling for logging.
public static void winEventlogMe(string logTxt, string logSrc, char entryType)
{
#region Log
//Log to event log
EventLog theEvent = new EventLog("WLConsumer");
theEvent.Source = logSrc;
if (entryType == 'e')
theEvent.WriteEntry(logTxt, EventLogEntryType.Error);
else if (entryType == 'i')
theEvent.WriteEntry(logTxt, EventLogEntryType.Information);
else if (entryType == 'w')
theEvent.WriteEntry(logTxt, EventLogEntryType.Warning);
else
theEvent.WriteEntry(logTxt, EventLogEntryType.Error);*/
#endregion
}
When I comment out the calls to winEventLogMe() method in the OnStart() method, the service starts without an error. So obviously something is wrong with the winEventLogMe() method.
Can someone please help me figure out whats the problem as I am totally clueless on how to solve this issue now.
thanx in advance :)
#nick_w I have edited my code as you suggested, the service started succesfully. But on Stopping it I got the following message:
Failed to stop service. System.ArgumentException: The source 'WLConsumer2012' is not registered in log 'ServiceStop'. (It is registered in log 'SeriviceStartup'.) " The Source and Log properties must be matched, or you may set Log to the empty string, and it will automatically be matched to the Source property.
at System.Diagnostics.EventLogInternal.VerifyAndCreateSource(String sourceName, String currentMachineName)
at System.Diagnostics.EventLogInternal.WriteEntry(String message, EventLogEntryType type, Int32 eventID, Int16 category, Byte[] rawData)
at System.Diagnostics.EventLog.WriteEntry(String message, EventLogEntryType type)
at WeblogicConsumerService.WeblogicConsumer.winEventlogMe(String logTxt, String logSrc, Char entryType) in C:\Program Files (x86)\CSI\WeblogicConsumerService\WeblogicConsumer.cs:line 139
at WeblogicConsumerService.WeblogicConsumer.OnStop() in C:\Program Files (x86)\CSI\WeblogicConsumerService\WeblogicConsumer.cs:line 70
at System.ServiceProcess.ServiceBase.DeferredStop()
here is the OnStop() method
protected override void OnStop()
{
winEventlogMe("Successful stop", "ServiceStop", 'i');
}
These event logs are starting to confuse me a lot. I have done the same method of logging in other services and never encountered such problems. How can I be getting these errors in this service yet its not much different from all the others I have done :(
I think this is your problem:
EventLog theEvent = new EventLog("WLConsumer");
Judging by the exception, I am thinking that WLConsumer is the name of the event source. What this means is that you might be better off with this:
EventLog theEvent = new EventLog(logSrc);
theEvent.Source = "WLConsumer";
This is just using the parameters the other way around.
If I do a little decompilation, there is a check like this:
if (!EventLogInternal.SourceExists(logName, machineName, true))
In your case I would think this check is returning true, meaning that it is trying to create a log named WLConsumer but failing because WLConsumer has been registered as an event source.
Edit:
When I have used the event log in the past, I wrote everything to the same combination of source and log. In your case you seem to be using a different combination of source and log each time you write an entry.
From MSDN (emphasis mine):
If you write to an event log, you must specify or create an event Source. You must have administrative rights on the computer to create a new event source. The Source registers your application with the event log as a valid source of entries. You can only use the Source to write to one log at a time. The Source can be any random string, but the name must be distinct from other sources on the computer. It is common for the source to be the name of the application or another identifying string. An attempt to create a duplicated Source value throws an exception. However, a single event log can be associated with multiple sources.
What I would suggest is this:
Use WLConsumer (or WLConsumer2012) as your source, and either
Define your own log, 'WLConsumerServiceEventLog` or something; or
Leave the log blank. They go into the Application log in this case.
Regardless, standard practice seems to be to do something like this prior to running your service for the first time, such as in an installer (copied straight from above link):
// Create the source, if it does not already exist.
if(!EventLog.SourceExists("MySource"))
{
//An event log source should not be created and immediately used.
//There is a latency time to enable the source, it should be created
//prior to executing the application that uses the source.
//Execute this sample a second time to use the new source.
EventLog.CreateEventSource("MySource", "MyNewLog");
Console.WriteLine("CreatedEventSource");
Console.WriteLine("Exiting, execute the application a second time to use the source.");
// The source is created. Exit the application to allow it to be registered.
return;
}
Note the point in the comments re latency. The logs are not necessarily created immediately, so it pays to code with this in mind. You could also use the EventLogInstaller to create the log. This may be the easier option if you are using an installer to deploy your service.
It is critical not to overload the on start method and in order to prevent service start failures, normally the onstart method launches the main code as a separate thread