I am using WF 4.5 with a custom tracking system.
The tracking system will create task records in a custom database.
The tracking participant will create a task in the database whenever a Receive activity enters the 'Executing' state.
The tracking parcitipant will mark the task as completed in the database whenever a Receive activity enters the 'Closed' state.
When completing the task I need to fill in the user who executed the task.
The receive activity will have a custom class with a user property in it (content parameter).
I want to obtain this user property from the tracking participant.
How do I go about this?
public class WorkflowSystemTrackingParticipant : TrackingParticipant
{
protected override void Track(TrackingRecord record, TimeSpan timeout)
{
ActivityStateRecord aRecord = record as ActivityStateRecord;
... // get the parameters values send to the receive activity
}
}
Just like you track states and variables you want to track arguments also.
In your particular case you would track the InArgument<string> which holds the user you want.
new ActivityStateQuery
{
Variables = { * },
States = { "Executing", "Closed" },
Arguments = { "UserArgument" }
}
Now you can get the UserArgument which is available on ActivityStateRecord:
protected override void Track(TrackingRecord record, TimeSpan timeout)
{
var aRecord = record as ActivityStateRecord;
var userName = aRecord.Arguments["UserArgument"] as string;
// do whatever you want with 'username'
}
Related
I am sending app usage analytics events to Fabric and Firebase. Together with the event, I am also sending another value (an example event type is font_selection and the value I pass is which font the user selects - this is a number that tells me which font was used). I was using Fabric events and I could see which fonts were being used more or less when I selected the font_selection event (I could see numbers for each different font).
Since the Fabric functionality is being moved to Firebase, I started checking the Analytics section in Firebase. Unfortunately I cannot find the above information in Firebase > Analytics > Events. I can see the event, font_selection but when I click on it I do not get the additional information I used to get in Fabric. Is there something I am missing or has this additional information been removed from Firebase?
This is still an issue for me. Here is how I'm sending the event into Firebase:
protected void Report(string id, Severity severity, string message = null, Exception exception = null)
{
try
{
var processedId = id ?? severity.ToString().ToLowerInvariant();
var values = new Dictionary<string, string>();
values.Add("severity", severity.ToString().ToLowerInvariant());
if (!string.IsNullOrWhiteSpace(message))
{
values.Add("message", message);
}
if (exception != null)
{
values.Add("exception", exception.Message);
values.Add("trace", exception.StackTrace);
}
SendEvent(values, processedId);
}
catch
{
// do nothing.
}
}
protected override void SendEvent(Dictionary<string, string> eventData, string id)
{
var firebaseAnalytics = FirebaseAnalytics.GetInstance(Android.App.Application.Context);
var bundle = new Android.OS.Bundle();
foreach(var pair in eventData)
{
bundle.PutString(pair.Key, pair.Value);
}
firebaseAnalytics.LogEvent(id, bundle);
}
During runtime, I call this successfully and I can see these event popping up in Firebase console:
But how do I display the rest of the properties that I have bundled with it? Here is what the console shows me in events:
I feel like I must be using it wrong or something. There is no UI to shows me a simple chronologically sorted table with events as they came in with properties they came with. I frankly don't understand what good is this tool to me.
I'm new to signalr and created a project with this sample to get the number of users on a specific website: Tutorial
This is running fine. My goal is to access the website only by one user, if a second user want to open the page he should be redirected. How can I do this?
If I check the users on the page and redirect if there are more than one then all users get redirected. Ok that what signalr should do.
userActivity.client.updateUsersOnlineCount = function (count) {
// Add the message to the page.
$('#usersCount').text(count);
if (count > 1) { window.document.location.href = "OPL.aspx"; }
};
How can I store the count in a datatype which I can access from code behind in the .cs? Thanks
For this, you need two client methods. The updateUsersOnlineCount have one job, which is to update users online for all to see. Then you need a second client side method called something like redirectTheUser to redirect the user.
In your SignalR hub, you would implement the OnConnected, OnReconnected, OnDisconnected events, to store (keep track of) the connection Ids, and when the count reaches a certain threshold, send the updateUsersOnlineCount to all clients with Clients.All.updateUsersOnlineCount(msg), but send the message withClients.Client(connectionId).redirectTheUser()` for all users above the threshold.
To illustrate:
public override Task OnConnected()
{
string name = Context.User.Identity.Name;
_connections.Add(name, Context.ConnectionId);
// send to all above threshold
if(_connections.Count > threshold)
SendRedirect(_connections.Skip(threshold));
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled)
{
string name = Context.User.Identity.Name;
_connections.Remove(name, Context.ConnectionId);
return base.OnDisconnected(stopCalled);
}
public override Task OnReconnected()
{
string name = Context.User.Identity.Name;
if (!_connections.GetConnections(name).Contains(Context.ConnectionId))
{
_connections.Add(name, Context.ConnectionId);
// send to all above threshold
if(_connections.Count > threshold)
SendRedirect(_connections.Skip(threshold));
}
return base.OnReconnected();
}
private void SendRedirect(IEnumerable<string> connectionIds)
{
foreach (var connectionId in connectionIds)
{
Clients.Client(connectionId).redirectTheUser();
}
}
I am doing my first tests with SignalR. I am toying with chat messages, but that's only a first step to replace all the polling from client to server which I have today on my site.
I have a lot of scenarios where I want to notify certain users either by their login or by their ID. The idea is that I am adding each user to two groups as soon as he connects. I do this in OnConnected and that event is called.
When I send a chat message, I have two modes: either public or personal. If it is personal the sender is notified and the recipient should be notified. The sender gets a message but the group never does. It seems to be impossible to found out how many members a group has.
Any ideas what's going wrong here?
public class GlobalHub:Hub
{
private Users user;
private void AuthenticateUser()
{
var ydc = new MyDataContext();
user = ydc.Users.First(u => u.Login == HttpContext.Current.User.Identity.Name);
}
public override Task OnConnected()
{
var ydc = new MyDataContext();
user = ydc.Users.First(u => u.Login == HttpContext.Current.User.Identity.Name);
Groups.Add(Context.ConnectionId, user.Login);
Groups.Add(Context.ConnectionId, user.ID.ToString());
return base.OnConnected();
}
public void SendChatMessage(string message, string recipient)
{
AuthenticateUser();
var cm = ChatController.AddChatMessage(user.Login, user.ID, recipient, tmessage);
if (recipient != "")
{
Clients.Caller.NewMessage(cm);
Clients.Group(recipient).NewMessage(cm);
}
else
{
Clients.All.NewMessage(cm);
}
}
}
It looks like that Groups.Add does not immediately join the connection to the group, but instead returns a Task, that needs to be started. Try returning the result of Groups.Add as result of OnConnectedMethod.
See also more detailed explanation at: https://stackoverflow.com/a/15469038/174638
I am using MassTransit request and response with SignalR. The web site makes a request to a windows service that creates a file. When the file has been created the windows service will send a response message back to the web site. The web site will open the file and make it available for the users to see. I want to handle the scenario where the user closes the web page before the file is created. In that case I want the created file to be emailed to them.
Regardless of whether the user has closed the web page or not, the message handler for the response message will be run. What I want to be able to do is have some way of knowing within the response message handler that the web page has been closed. This is what I have done already. It doesnt work but it does illustrate my thinking. On the web page I have
$(window).unload(function () {
if (event.clientY < 0) {
// $.connection.hub.stop();
$.connection.exportcreate.setIsDisconnected();
}
});
exportcreate is my Hub name. In setIsDisconnected would I set a property on Caller? Lets say I successfully set a property to indicate that the web page has been closed. How do I find out that value in the response message handler. This is what it does now
protected void BasicResponseHandler(BasicResponse message)
{
string groupName = CorrelationIdGroupName(message.CorrelationId);
GetClients()[groupName].display(message.ExportGuid);
}
private static dynamic GetClients()
{
return AspNetHost.DependencyResolver.Resolve<IConnectionManager>().GetClients<ExportCreateHub>();
}
I am using the message correlation id as a group. Now for me the ExportGuid on the message is very important. That is used to identify the file. So if I am going to email the created file I have to do it within the response handler because I need the ExportGuid value. If I did store a value on Caller in my hub for the web page close, how would I access it in the response handler.
Just in case you need to know. display is defined on the web page as
exportCreate.display = function (guid) {
setTimeout(function () {
top.location.href = 'GetExport.ashx?guid=' + guid;
}, 500);
};
GetExport.ashx opens the file and returns it as a response.
Thank you,
Regards Ben
I think a better bet would be to implement proper connection handling. Specifically, have your hub implementing IDisconnect and IConnected. You would then have a mapping of connectionId to document Guid.
public Task Connect()
{
connectionManager.MapConnectionToUser(Context.ConnectionId, Context.User.Name);
}
public Task Disconnect()
{
var connectionId = Context.ConnectionId;
var docId = connectionManager.LookupDocumentId(connectionId);
if (docId != Guid.Empty)
{
var userName = connectionManager.GetUserFromConnectionId(connectionId);
var user = userRepository.GetUserByUserName(userName);
bus.Publish( new EmailDocumentToUserCommand(docId, user.Email));
}
}
// Call from client
public void GenerateDocument(ClientParameters docParameters)
{
var docId = Guid.NewGuid();
connectionManager.MapDocumentIdToConnection(Context.ConnectionId, docId);
var command = new CreateDocumentCommand(docParameters);
command.Correlationid = docId;
bus.Publish(command);
Caller.creatingDocument(docId);
}
// Acknowledge you got the doc.
// Call this from the display method on the client.
// If this is not called, the disconnect method will handle sending
// by email.
public void Ack(Guid docId)
{
connectionManager.UnmapDocumentFromConnectionId(connectionId, docId);
Caller.sendMessage("ok");
}
Of course this is from the top of my head.
I want to be able to have individual users send messages to each other using SignalR, therefore I need to send to a Specific Client ID. How can I define the client ID for a specific user at the start of the session - say a GUID Primary Key for the user?
Replace the IConnectionIdFactory with your own https://github.com/SignalR/SignalR/wiki/Extensibility.
Sample usage:
http://www.kevgriffin.com/maintaining-signalr-connectionids-across-page-instances/
EDIT: This is no longer supported in the latest versions of SignalR. But you can define a user id for a specific connection using the new IUserIdProvider
In SignalR version 1, using the Hubs approach, I override the Hub OnConnected() method and save an association of a .NET membership userId with the current connection id (Context.ConnectionId) in a SQL database.
Then I override the Hub OnDisconnected() method and delete the association between the .NET membership userId and the current connection id. This means, on a page reload, the userId/connectionId association will be up-to-date.
Something along the lines of:
public class MyHub : Hub
{
private MembershipUser _user
{
get { return Membership.GetUser(); }
}
private Guid _userId
{
get { return (Guid) _user.ProviderUserKey; }
}
private Guid _connectionId
{
get { return Guid.Parse(Context.ConnectionId); }
}
public override Task OnConnected()
{
var userConnectionRepository = new UserConnectionRepository();
userConnectionRepository.Create(_userId, _connectionId);
userConnectionRepository.Submit();
return base.OnConnected();
}
public override Task OnDisconnected()
{
var userConnectionRepository = new UserConnectionRepository();
userConnectionRepository.Delete(_userId, _connectionId);
userConnectionRepository.Submit();
return base.OnDisconnected();
}
}
Then when I need to trigger a SignalR event for a specific user, I can work out the connectionId from the database association(s) with the current userId - there may be more than one association if multiple browser instances are involved.
The SignalR Client Side documentation outlines the following:
connection.id
- Gets or sets the client id for the current connection
This certainly indicates that one should be able to set the clientID client side, without all the above plumbing. Is this not working? If working, how would this line of code look like?