terminate persisted Workflow after specific time - workflow-foundation-4

i use windows Workflow fundation for handle many step of wizard, and after per step completed, workflow persisted and control back to user for new step, in some case may be user not completed all steps of wizard, in this case i want terminated uncompleted wizard, for this i want use a timeout time, but i do not know how do this with workflow.
another problem is i want set userId or username of current user per instance of workflow but i do not know how do this, plz guide me, tnx;

You Cancel or Terminate workflows from outside of the workflow. You do the work at the level where you created the WorkflowApplication.
Assuming you start your workflow as an application like this:
WorkflowApplication _wfApp = new WorkflowApplication(new YourWorkFlow(), inParams);
_wfApp.Run();
Then you have a reference to the WorkflowApplication as wfApp.
So you can set up a timer and then use the reference when the timer fires to terminate the workflow.
Thread.Sleep(TimeSpan.FromSeconds(1));
wfApp.Terminate(new ApplicationException("Terminating the workflow."));
You can detect the termination by capturing the workflow completed event.
wfApp.Completed = delegate(WorkflowApplicationCompletedEventArgs e)
{ Do your clean up code here }
See http://msdn.microsoft.com/en-us/library/dd781832(v=vs.110).aspx

As regards your second question, are you asking how you pass the userId or username of the current user into a workflow when it starts?
If so then you create a dictionary holding the parameters you want to use and you pass it into the WorkflowApplication object before you run the workflow.
IDictionary<string, object> inParams = new Dictionary<string, string>
{
{"UserID", "12345678" },
{"UserName", "Bob" }
};
WorkflowApplication _wfApp = new WorkflowApplication(new YourWorkFlow(), inParams);
_wfApp.Run();
You must set up your workflow so that it has In Arguments called UserID and UserName of type string. You don't need to always use string. You can pass in any Type you want.

Related

does returning object in insert method violate cqrs pattern?

I have implemented MediatR in my asp.net core web api application.
Our controllers simply send a command or query to MediatR and return the result.
[HttpPost]
public async Task<IActionResult> CreateQuestion(CreateQuestionCommand command)
{
await Mediator.Send(command);
return Ok();
}
Due to the CQRS pattern that says commands should not return any value we don't return any value in our MediatR commands.
everything was normal since we decided to write some BDD tests.
In our BDD tests there is a simple scenario like this:
Scenario: [Try to add a new Question]
Given [I'm an authorized administrator]
When [I create a new Question with Title '<Title>' and '<Description>' and '<IsActive>' and '<IndicatorId'>]
Then [A question with Title '<Title>' and '<Description>' and '<IsActive>' and '<IndicatorId'> should be persisted in database]
Examples:
| Title | Description | IsActive | IndicatorId |
| This is a test title | this is a test description | true | 3cb23a10-107a-4834-8c1a-3fd893217861 |
We set Id property of Question object in its constructor. which means we don't know what Id we have set to this newly created Question and therefore we can't read it after adding it to database in a test environment.
my question was how to test it in BDD?
this is my test step implementation:
[When(#"\[I create a new Question with Title '([^']*)' and '([^']*)' and '([^']*)' and '([^']*)'>]")]
public async void WhenICreateANewQuestionWithTitleAndAndAnd(string title, string description, bool isActive, Guid indicatorId)
{
var command = new CreateQuestionCommand
{
Title = title,
Description = description,
IndicatorId = indicatorId
};
await questionController.CreateQuestion(command);
}
[Then(#"\[A question with Title '([^']*)' and '([^']*)' and '([^']*)' and '([^']*)'> should be persisted in database]")]
public void ThenAQuestionWithTitleAndAndAndShouldBePersistedInDatabase(string title, string description, string isActive, string indicatorId)
{
//? how to retrieve the created data. We don't have the Id here
}
How can I retrieve the added Question?
Should I change my command handlers to return my object after inserting it to database?
And if I do so, wouldn't it be a CQRS violation?
Thank you for your time.
There's a couple of ways to go about this, but it depends on how you actually expect the system to behave. If you're doing BDD you should focus on the kind of behaviour that would be observable by real users.
If you're implementing a system that allows users to create (and save, I infer) questions in a (Q&A?) database, then should they not have the ability to view or perhaps edit the question afterwards?
As the OP is currently phrased, the trouble writing the test seems to imply a lacking capability of the system in general. If the test can't identify the entity that was created, then how can a user?
The CreateQuestion method only returns Ok(), which means that there's no data in the response. Thus, as currently implemented, there's no way for the sender of the POST request to subsequently navigate to or retrieve the new resource.
How are users supposed to interact with the service? This should give you a hint about how to write the test.
A common REST pattern is to return the address of the new resource in the response's Location header. If the CreateQuestion method were to do that, the test would also be able to investigate that value and perhaps act on it.
Another option is to return the entity ID in the response body. Again, if that's the way things are supposed to work, the test could verify that.
Other systems are more asynchronous. Perhaps you only put the CreateQuestionCommand on a queue, to be handled later. In such a case I'd write a test that verifies that the command was added to the queue. You could also write a more long-running test that then waits for an asynchronous message handler to process the command, but if you want to test something like, you also need to deal with timeouts.
In a comment, you write:
I think he might add the question and then read all questions from database and see if his question is amongst them. He'd probably test without knowing Id.
Do you expect real users of the system to spelunk around in your database? Unless you do, the database is not part of a system's observable behaviour - it's an implementation detail. Thus, behaviour-driven design shouldn't concern itself with the contents of databases, but how a system behaves.
In short, find out how to observe the behaviour you want the system to have, and then test that.

What I misunderstood about the Mediator Pattern?

I'm new here and a beginner architect. I'm helping the company I work to design our new product and since I start it, I read a lot about but is never enough.
I decided to use Mediator Pattern (with Mediatr) to call my application layer.
It's cool and I got how to work with it sometimes, but sometimes I get confused.
For example, when we publish a document on our new product, we uses a RequestHandler to do everything and check all rules it needs, it's fine and works like a charm, but, when I want just a quick data, it looks likes too much for just a simple thing.
As an example, every time the user do any kind of action on my web application, I have to check if he is still logged. We have single login per user, so, if the same user connect anywhere else, the older session expires. We do it by saving on database.
On every action of my app, I go to base and check if the session key is the same, as bellow.
var sessionKey = bibliotecaCookie.Value;
var mediator = controller.GetMediator();
var isUserSessionKeyValidRequest = new IsUserSessionKeyValidRequest()
{
sessionKey = sessionKey
};
var isValidSession = mediator.Send(isUserSessionKeyValidRequest).Result;
if (!isValidSession)
throw new UnauthorizedAccessException();
So, I have a RequestHandler (a Handler and a Request which returns a bool) just to check if the user session is ok.
When the handler catches this request, it goes to database and execute a simple rule which is "Is the passed session the same as the stored session key?".
Is it right? Is it the right approach? Did I understand it right?
Thanks in advance guys

Partial Realms - Why and When are They Created?

I am using Realm and building a Swift mobile app. I am really struggling to understand why and when Partial realms are created.
Here is my scenario:
a user logs in to the app and is brought to the first view controller.
In the first view controller in view did load, I am executing a query to get the current user, subscribing to the query and adding an observer to let me know when the data is synced:
let currentUserArr = realm.objects(DBUser.self).filter("id == %#", userId)
self.subscription = currentUserArr.subscribe(named: "current user")
self.subscriptionToken = self.subscription.observe(\.state, options: .initial) { state in
switch state {
case .creating:
print("creating")
case .pending:
print("pending")
case .complete:
print("complete")
self.artist = currentUserArr[0]
case .invalidated:
print("invalidated")
case .error(let err):
//seal.reject(err)
print(err)
}
}
This makes sense that if I check Realm Cloud, I have a new partial realm created with path as:
/db/__partial/DyeOy3OR4sNsqMi2OmDQQEzUa8F3/~7f11cf52
However, here is where my confusion starts. I log the user out. I log back in and again the code above executes. My thought would be that Realm would just reuse the partial already created, but instead it creates an entirely new partial.
/db/__partial/DyeOy3OR4sNsqMi2OmDQQEzUa8F3/~8bc7bc49
Is this by design or should I somehow be reusing partials rather than having a new one created every time a query is executed (even if it is executed by the same user)?
I have posted on Realm Forums as well:
https://forums.realm.io/t/realm-platform-realm-path-partial-s/2833
I don't believe I was actually logging the current sync user out. Upon further testing, once I did log out and log back in, the existing partial was re-used. This is a non-issue.

Automatic activity not performing

Created a workflow with basic as below.
Created a calss library, used ProgId, set comvisible true and registerd the assembly in the Tridion server.
This is the way i have tested:
Created a component
Finished the activity from the work list.
Navigated to the "Global Work list" and finished the Reviewer activity by myself by choosing the "Back to Author" step and clicked the "Finish" button.
The item is not moved to the author. but when i finish the activity again from the global work list, the item moved to author.
It seems that my code is not performing the activity because i tried removed the below VB script code and tried with the default automatic script code.
' Script for Automatic Activity Content Manager Workflow
FinishActivity "Automatic Activity Finished"
It behaves the same as above. so i decided my code is not worked. Can any one please help on this?
Below is the VBScript I used in the script box of "Back to Author":
Option Explicit
Dim workflowHandler
Set workflowHandler = CreateObject("CoreComponentWorkflow.WorkflowHandler");
If Not workflowHandler Is Nothing Then
Call workflowHandler.MoveBackToActivity(Cstr(CurrentWorkItem.ID, "Create or Edit Component")
End If
Set workflowHandler = Nothing
Below is the C# Code:
public void MoveBackToActivity(string workitemid, string strActivitytoMove)
{
try
{
Session session = new Session();
WorkItem workitem = new WorkItem(new TcmUri("workitemid"), session);
ActivityInstance currentactivity = workitem.Activity as ActivityInstance;
ProcessInstance procInstance = currentactivity.Process as ProcessInstance;
IEnumerable<ActivityInstance> ieActivities = procInstance.Activities
.Select (s => s)
.Where (w => w.Title.IndexOf(strActivitytoMove) !=-1)
.OrderByDescending(w =>w.StartDate);
if (ieActivities != null && ieActivities.Count<ActivityInstance>() > 0)
{
ActivityInstance targetactivity = ieActivities.ElementAt(0);
User lastperformuser = targetactivity.Performers.ElementAt(targetactivity.Performers.Count<User>() - 1);
ActivityFinish finish = new ActivityFinish(targetactivity.FinishMessage, lastperformuser, workitem.Session);
currentactivity.Finish(finish);
}
}
catch (Exception ex)
{
throw ex;
}
}
Be aware that you are using an API that is NOT supported in Automatic Activities. The only processes where you are allowed to use TOM.NET are Event System handlers and Template Building Blocks as documented here.
Automatic Workflow Activities - if not developed with VBScript - must use the CoreService interface.
The good news is that I know for a fact this works - plenty of people got it to work in many implementations. The bad news (for you) is that the error is in your code. Have you tried debugging/step-by-step through your code yet? You can attach to to the workflow process (cm_wf_svc.exe) and figure out what's wrong with the code much faster than we can.
Here's a really simple snippet to finish an activity with CoreService:
ActivityFinishData activityFinish = new ActivityFinishData
{
Message = "Automatically Finished from Expiration Workflow Extension"
};
ActivityInstanceData activityInstance =
(ActivityInstanceData)processInstance.Activities[0];
client.FinishActivity(activityInstance.Id, activityFinish, readOptions);
BTW - If you intended to use TOM.NET anyway, why did you bother asking which API to use?
Following the Nuno's answer, yes you should change the code to use TOM or Core Services. TOM .Net is not supported because it is using a different thread apartment than the underlying technology we use for workflow (COM).
About the issue I have checked that you are calling the activity like this.
Call workflowHandler.MoveBackToActivity(Cstr(CurrentWorkItem.ID, "Create or Edit Component")
It looks like the activity name is not matching. there are some strange characters between "Edit" and "Component"
I hope this helps.
Automatic activities are executed by the Workflow agent service. An Assigned state may indicate that it's just not being picked up by the service. Is your service running correctly, and are things like queue notifications set up properly?

SynchronizationContext and Bookmarks in WF4

I am running workflows under asp.net and using SynchronizationContext to make the page "wait" for the workflow. Here is how I run the workflow instance under asp.net:
var workflowApplication = new WorkflowApplication(activity);
SynchronizationContext syncContext = SynchronizationContext.Current;
workflowApplication.Completed = delegate { syncContext.OperationCompleted(); };
workflowApplication.SynchronizationContext = syncContext;
syncContext.OperationStarted();
workflowApplication.Run();
In one of the activities I use a bookmark. Now I want the page processing to continue whenever I call CreateBookmark. I tried calling SynchronizationContext.Current.OperationCompleted() before setting the bookmark but that crushes asp.net site when the workflow resumes and completes (I think the workflow instance calls OperationCompleted again when it completes and the error raises)
How can I work with bookmarks under Asp.Net, any ideas?
The Completed Property isn't being called when you persist the workflow with a bookmark, try adding this line after you've set up the completed property:
workflowApplication.PersistableIdle = args => {
syncContext.OperationCompleted();
return PersistableIdleAction.None;
};
This should then be called instead of completed, and the ASP.NET code should regain control. Change the PersistableIdleAction enum to whatever is required, I've just picked None for example code.

Resources