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.
Related
I implemented a messaging system for my android application. Here is how it works :
The Users sends a message
The message is add in the Database and then add to the listview Adapter
To get the messages from the other users, I implement a background thread that "poll" the server. If an older message is found, it is then add to the listview adapter if the message is not already in the adapter.
The problem that I'm facing is that sometimes the message send by the user is displayed twice. I think that I understand the problem : the polling gets the new added message as not already in the listview and add it to the listview adapter. Is there a way to avoid that behaviour?
Here is how I checked if the polled message is already in the adapter :
public class MessageListViewAdapter : BaseAdapter
{
List<Model.Message> messages = new List<Model.Message>();
Context context;
public MessageListViewAdapter(Context context,List<Model.Message> messages)
{
this.context = context;
this.messages = messages;
}
public void add(Model.Message message)
{
if (!messages.Contains(message))
{
this.messages.Add(message);
NotifyDataSetChanged(); // to render the list we need to notify
}
}
public override int Count =>messages.Count;
public override Java.Lang.Object GetItem(int position)
{
return null;
}
public override long GetItemId(int position)
{
return position;
}
Let me know if you need other informations, maybe my explanation is not clear or not complete enough.
Thanks in advance,
Lio
What you should do is separate the listview from the storage of the messages.
Instead of putting items into a listview, you should maintain a list of messages in an array. When you type a message you can add it to the array, along with a date time stamp or GUID. When you poll, you also add entries to the array if they don't exist. Create a method called AddItemToListView() or similar to do this
Then keep population of the list view separate - create a method called UpdateListView() and call it after polling and after you type a message and add it.
Inside AddItemToListView() you can put logic to check if the item is already in the list, and if it is, don't add it. By either comparing the date time, or the GUID.
I would definitely check the Date or a GUID to compare messages because anything else is not reliable, especially object comparisons which is what it looks like you're doing.
Introduction
Currently I'm trying to create a Bot Framework application using the Microsoft Bot Framework v4.
Structure of program
We currently have the following setup:
The root of the bot class is named: SubDialogBotBot
Within the SubDialogBot we create a new Dialog named ParentDialog. This Dialog is responsible for reacting to a specific Intent.
We then start a new Dialog from the ParentDialog named ChildDialog. This child dialog will be responsible for asking the user a question based on arguments passed by ParentDialog.
After this question completed we want to return to the ParentDialog and continue the flow.
In this example we want to re-use the ChildDialog from all kinds of different intents as the code in here is exactly the same. The only thing that changes is the questions that have to be asked to the user.
Problem
When the ChildDialog completes the 'flow' is never returned to the ParentDialog.
We also tried to have the Dialog following after the ChildDialog ID set to something specific and then call this using Context.BeginDialog(....) from the ChildDialog. However because apparently the dialog was added to the ParentDialog and not to the ChildDialog it can't find this by the id.
Github repository reproducing the problem
https://github.com/devedse/SubDialogBotReproduction
First, this is an awesomely prepared question, thank you... especially for sharing the code.
Now, the good news is I don't think I see any problem with your dialogs. The problem is actually in your bot's OnTurnAsync. You are only ever calling BeginDialogAsync on your ParentDialog. Every single activity is going to come in through your OnTurnAsync and that means you're responsible for handling re-entrancy into the dialog stack. This means that, you need to check for an active dialog and, if there is one, you need to be calling ContinueDialogAsync instead to resume from where the discussion left off. Here's your current OnTurnAsync with the extra checks added:
public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
{
// Create a dialog context
var dc = await Dialogs.CreateContextAsync(turnContext);
// Handle Message activity type, which is the main activity type for shown within a conversational interface
// Message activities may contain text, speech, interactive cards, and binary or unknown attachments.
// see https://aka.ms/about-bot-activity-message to learn more about the message and other activity types
if (turnContext.Activity.Type == ActivityTypes.Message)
{
// If there's no active dialog, begin the parent dialog
if(dc.ActivDialog == null)
{
await dc.BeginDialogAsync(nameof(ParentDialog));
}
else
{
await dc.ContinueDialogAsync();
}
// Save the new turn count into the conversation state.
await _accessors.ConversationState.SaveChangesAsync(turnContext);
}
else
{
await turnContext.SendActivityAsync($"{turnContext.Activity.Type} event detected");
}
}
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);
I am completely new in Windows Phone development and I am trying to write an app that retrieves the data from a server and displays them to the user. I have several resources on the server, lets say User, Quest and Activity. I use RestSharp lib to retrieve the data from the server.
Example of Get User:
public void Get(String id, LifeHunt.MainPage.UserReady userReady)
{
var client = new RestClient(Deployd.REST_URL);
var request = new RestRequest(resource + "/{id}", Method.GET);
request.AddUrlSegment("id", id);
client.ExecuteAsync<User>(request, response =>
{
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
userReady(callback.Data);
}
});
}
Once the user is retrieved, I call the userReady method I passed as callback and get the user to MainPage to display it.
Now, I have to repeat the whole process for all CRUD (Insert, Get, GetAll, Update, Delete) functions for all Users, Quest and Activity. That means I will need 15 different callback methods, which I think is not a good software design.
The other way would be just one callback method and check the type of parameter passed in the callback method. However I do not think this is a nice solution as well.
I was thinking about something like creating a generic interface for CRUD, implement it by all the User, Quest and Activity classes:
interface ICRUD<T>
{
void GetAll(GenericCallback callback);
void Get(GenericCallback callback);
void Add(T item, GenericCallback callback);
void Remove(String id, GenericCallback callback);
void Update(T item, GenericCallback callback);
}
However I do not really know how to implement it and even if it is a good way. Could somebody suggest me a good design solution?
Use MVVM, create a ViewModel that would hold all the data you need and bind it to the View. Then create Service class with methods directly returning the data you need (no callbacks). Create a instance of this Service in the ViewModel and call the methods to get and fill the data when needed.
I'm developing custom workflow step for MS CRM 2011.
I wondering is it possible to retrieve NetworkCredentials object representing current user who running this workflow?
Does this information present in CodeActivityContext object?
Here is how Activity definition looks like:
public class CustomActivity : CodeActivity
{
protected override void Execute(CodeActivityContext context)
{
...
}
}
Nope. I believe that you would not be able to do that. All the async jobs are done in security context of account that is used for AsyncService login.
you can find who running this workflow If you use these lines:
protected override void Execute(CodeActivityContext context)
{
IWorkflowContext _Context = context.GetExtension<IWorkflowContext>();
IOrganizationServiceFactory _IOrganizationServiceFactory = context.GetExtension<IOrganizationServiceFactory>();
IOrganizationService xrmService = _IOrganizationServiceFactory.CreateOrganizationService(_Context.InitiatingUserId);
}
Something that could help: _Context.InitiatingUserId
and you must register your plugin in plugin registration apart from calling user
I think it depends on the type of workflow, you are using. For example I used StateMachine type, where in OnResumeBookmark method could be send object variable, where you can put any structure as interaction with user, who resumed workflow. After resuming you can set some workflow variable and use it as you wish.
Another way is use Workflow arguments of given type, when creating new Workflow instance.
What do you want to do with this credentials?