Let's assume I have the next pseudo code to implement Command based change in terms of CQRS (actually, Event Sourcing is questionable as well) in my WebApi project:
public IHttpActionResult ChangeVendor(ChangeVendorModel changeModel)
{
/* 1 */ // user input validation
/* 2 */ var changeCommand = changeModel.MapTo<ChangeVendorCommand>();
/* 3 */ bus.Send(changeCommand); // start the change processing
/* 4 */ return Ok();
}
The explanation:
We perform a basic user input validation (as string length or only
positive numbers) but not a business validation (as this Vendor is
in the black list).
We convert the input model to a command for a bus.
We send the prepared change command through the bus to be processed.
By this we mean the change was applied and a domain model is
available for the further manipulations.
The questions:
a. The bus processing is asynchronous. How can I be sure (after step 4)
that my changes were applied and the app is ready to render success
view displaying a changed record from a database designed for
querying purposes?
b. Let's say the record version conflict happened (data violation) or a model was not passed through the business rules (domain violation). How can I instantly notify a user about this from the bus? In a bad designed system, a user could see a successful result because we successfully scheduled the change on the bus and later on they could see the notification with an error when the attempt to apply the actual change was made.
As I suggested in the comments you could wait for the event which signals completion and only return to the user when this is received. Some pseudo code:
public IHttpActionResult ChangeVendor(ChangeVendorModel changeModel)
{
var changeCommand = changeModel.MapTo<ChangeVendorCommand>();
bus.Send(changeCommand); // start the change processing
var replyReceived=false;
bool success = false;
while(!replyreceived)
{
Task vendorChanged = Task.Factory.StartNew(()=>
{
var reply=bus.Receive<VendorChanged>());
if(reply.CorrelationToken==changeCommand.CorrelationToken)
{
replyReceived=true;
success=true;
}
},SomeTimeout);
Task vendorChangedFailed = Task.Factory.StartNew(()=>
{
var reply=bus.Receive<VendorChangeFailed>());
if(reply.CorrelationToken==changeCommand.CorrelationToken)
{
replyReceived=true;
success=false;
}
},SomeTimeout());
Task.WaitAny(new Task[]{vendorChanged,vendorChangeFailed});
}
if(success)
{
return Ok();
}
else
{
return ChangeVendorFailed();
}
}
obviously the receive needs to be on its own subscription, to ensure it doesn't take replies for other instances, and you may be able to create the subscription to receive only messages with the correct correlation token or other identifying property, but this gives you sopme idea of one way to skin this cat and make your tasky async workflow look syncronous to the user
Related
I just worked my way through this MS Learn Tutorial regarding SignalR in Blazor.
At the end of the tutorial, you get a program that can have multiple clients hooked up to a "ChatHub" to send and receive messages, like a "Townsquare-Chatroom"
While testing I realized, that if you send some messages and afterward create a new client, the new client does not display the previously send messages. This is because every client stores its received messages locally as shown here:
#code{
// ...
private List<string> messages = new();
// ...
}
I decided to implement such a feature.
To do so, I created ChatLog.cs which is supposed to log the messages for all clients instead of saving them inside of each individual client:
public class ChatLog
{
private List<string> _messages= new List<string>();
public List<string> Messages
{
get { return _messages; }
set
{
_messages = value;
}
}
}
Of course, I also had to make some changes inside of index.razor to make things work:
I added a new service in program.cs as singleton
==> Program.cs
// ...
builder.Services.AddSingleton<ChatLog>();
// ...
and injected ChatLog into my index.razor
==> Index.razor
// ...
#inject ChatLog ChatLogger
// ...
I changed the code in index.razor #code to add the messages to ChatLog.Messages instead of the "local" messages-List
protected override async Task OnInitializedAsync()
{
// Change
if(ChatLogger.Messages is null)
{
ChatLogger.Messages = new();
}
hubConnection = new HubConnectionBuilder()
.WithUrl(NavManager.ToAbsoluteUri("/chathub"))
.WithAutomaticReconnect()
.Build();
hubConnection.On<string, string>("ReceiveMessage", (user, message) =>
{
var formattedMessage = $"{user}: {message}";
// Change
ChatLogger.Messages.Add(formattedMessage);
InvokeAsync(StateHasChanged);
});
await hubConnection.StartAsync();
}
Now I run into a new problem.
Since the event
hubConnection.On<string, string>...
is called by every client, and all new messages get added into ChatLog.Messages X-times (x == amount of active clients).
I just can't think of a way to avoid this problem and only log every message exactly once.
Can someone help me?
Thanks in advance and sorry for the long explanation. Maybe someone can also help shorten it?
EDIT
To clarify the problem: Since the messages get added to the messages List inside of the event (as shown above), every instance (or every tab of the website) adds the message, resulting in multiple (and unwanted) adds.
E.g.
Two clients
Message "Hello" was sent once but added twice
Message "Ciao" was sent twice but added four times
From what I can gather this is more a learning exercise than something you're actually planning on using in a production environment, so we can ignore the fact that this isn't really a very robust implementation.
In any case, a simply solution would be to have the sender of the message store it in the messagelog, instead of storing it upon reception.
Taking from the tutorial you followed:
using Microsoft.AspNetCore.SignalR;
namespace BlazorServerSignalRApp.Server.Hubs
{
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
// STORE YOUR MESSAGE IN YOUR MESSAGE LOG HERE
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
}
You should be able to inject your MessageLog service into the ChatHub in order to access it from there. (If I'm understanding your project structure correctly)
I'm writing the code for a game whose server-side is totally based on Firebase. I expect to use Auth, Database, InstanceID, Messaging and Cloud Functions in the game.
Being a novice C# Programmer, I encountered with C# "Tasks" first time with Firebase.
I'm going to use Database for a lot of times (like Score Update, Friends Requests, Chat, Friend Did this, Friend Did that).
I mostly feel comfortable with Singleton Pattern (GameManagers, EnemyManagers, SoundManagers etc..).
But with Firebase, since most of its calls are asynchronous and implemented via Tasks. I think I need to workaround differently to implement Managers.
For example, I need to send a Friend Request to a specific friend. The UIManager is a script that deals with UI events etc. I'd like to call Method from this script to another Manager (say FriendsManager). But I need to first check if this friend is already friend of mine from Database or Not? So, what I would do is
class UIManager
{
void OnFriendRequestClicked(string friendId)
{
bool userExists = FriendsManager.instance.UserExists(friendId);
if(userExists)
// Proceed with Sending Request
FriendsManager.instance.SendRequest(friendId);
else
// Show a Dialogue that User ID is invalid
ShowError("User Id is invalid");
// NOTE: The above code block of "condition" is executed before
// the UserID is validated from FriendsManager
// I know its because of Task. But how can I alter this code
// to do something in the similar pattern?
}
}
class FriendsManager
{
bool UserExists(string userIdToCheck)
{
reference.Child("users").Child(userIdToCheck).GetValueAsync().ContinueWith(
task=>
{
if(task.IsCompleted)
{
if(task.Result == null)
return false; // (expected) Return false to Method "UserExists"
else
return true; //(expected) Return true to Method "UserExists"
// But this won't actually return "bool" to the method,
// it actually returns to its own "Task"
//NOTE: -> How to Return from here to the Method?
)};
}
Data is loaded from Firebase asynchronously. Instead of waiting/blocking while the data is being loaded, the app continues. And then when the data is available, it calls your callback.
You can most easily see this with some logging statements:
Debug.Log("Before starting to load data");
reference.Child("users").Child(userIdToCheck).GetValueAsync().ContinueWith(task=> {
Debug.Log("Data loaded");
});
Debug.Log("After starting to load data");
When you run this code it logs:
Before starting to load data
After starting to load data
Data loaded
That is probably not what you expected, but explains perfectly why you can't return a value from within the callback: the UserExists has already finished at that point.
This means that any code that needs access to the data from the database, must be inside the ContinueWith block (or be called from there).
The simplest approach is to move the code from your OnFriendRequestClicked into UserExists:
bool UserExists(string userIdToCheck) {
reference.Child("users").Child(userIdToCheck).GetValueAsync().ContinueWith(task=>
{
if(task.IsCompleted)
{
if(task.Result == null)
ShowError("User Id is invalid");
else
FriendsManager.instance.SendRequest(friendId);
)};
}
You can then call this function without the if after it.
The above approach works great, but means that your UserExists method is no longer reusable in different cases. To make it reusable again, you can pass your own callback interface into UserExists.
For example, using Task:
bool UserExists(string userIdToCheck, Action<bool> callback) {
reference.Child("users").Child(userIdToCheck).GetValueAsync().ContinueWith(task=>
{
if(task.IsCompleted)
{
if(task.Result == null)
callback(false);
else
callback(true);
)};
}
And then to invoke it:
FriendsManager.instance.UserExists(friendId, userExists => {
if(userExists)
FriendsManager.instance.SendRequest(friendId);
else
ShowError("User Id is invalid");
})
Essentially, I'd really like some specific commands for my Discord Bot, but I have very little experience with C#. My main goals are:
A welcome message to newcomers. (Privately, to the new user. Nobody else sees it but the newcomer via server)
Wait for a specific message to be typed. Once this message is typed, the bot adds a role to the user, and delete's the user's message that was typed (so that following newcomer won't see the message and using it instead of reading the rules like I intend for them to see first.)
A timer function that enables as soon as a new member joins. This timer is to go on for 3 days and if the new user does not fulfill the 2nd task in that time period, they will be kicked until they find a new invite.
Anything other than the specific message typed will be tried 3 attempts. if the user does not type the correct word into the input, they will be warned each time, until the attempt number reaches 3. After it exceeds 3, they will be kicked until they find a new invite.
Here is my current list of commands:
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
namespace BooleanBot.Modules
{
public class commands : ModuleBase<SocketCommandContext>
{
[Command("help")]
public async Task help()
{
var a = new Discord.EmbedBuilder();
a.WithTitle("Commands");
a.WithDescription("General Commands\n-- .help // Gives list of commands to use");
Discord.IDMChannel gencom = await Context.Message.Author.GetOrCreateDMChannelAsync();
await gencom.SendMessageAsync("", false, a);
await gencom.CloseAsync();
}
[Command("kick")]
[RequireBotPermission(Discord.GuildPermission.KickMembers)]
[RequireUserPermission(Discord.GuildPermission.KickMembers)]
public async Task KickAsync(Discord.IGuildUser user, [Remainder] string reason)
{
if (user.GuildPermissions.KickMembers)
{
var b = new Discord.EmbedBuilder();
b.WithTitle("User Kicked");
b.WithDescription(user.Username + "was kicked.");
await Context.Channel.SendMessageAsync("", false, b);
await user.KickAsync();
}
}
[Command("postwelcome")]
public async Task welcome()
{
var b = new Discord.EmbedBuilder();
b.WithTitle("Welcome to the Anthamation Server! I'm Antha-bot, the housekeeper! My server prefex is !Yo. Let's get started!");
b.WithDescription("Before you can do ANYTHING, you must go to #rules-and-access channel and read through the rules first! You will also find instructions on how to access the channels! PLEASE NOTE: If you are not a verified member within 3 days or type in something OTHER than the desired answer, you will be kicked automatically. See you on the other side!");
await Context.Channel.SendMessageAsync("", false, b);
}
}
}
Very sorry if this seems too lengthy for Stackoverflow. I have no where else to turn to.
With your welcome message, you need to subscribe to the UserJoined action. Somewhere else in your code I assume you have something along the lines of client.MessageReceived += client_MessageReceived;. To subscribe to the UserJoined action you need to add client.UserJoined += client.UserJoined;. Then once you have done that to it you can execute the client_UserJoined() code.
private async Task client_UserJoined(SocketGuildUser arg)
{
// whatever you put here will execute when a user joins.
}
I have problem in when user post the data. Some times the post run so fast and this make problem in my website.
The user want to register a form about 100$ and have 120$ balance.
When the post (save) button pressed sometimes two post come to server very fast like:
2018-01-31 19:34:43.660 Register Form 5760$
2018-01-31 19:34:43.663 Register Form 5760$
Therefore my client balance become negative.
I use If in my code to check balance but the code run many fast and I think both if happen together and I missed them.
Therefore I made Lock Controll class to avoid concurrency per user but not work well.
I made global Action Filter to control the users this is my code:
public void OnActionExecuting(ActionExecutingContext context)
{
try
{
var controller = (Controller)context.Controller;
if (controller.User.Identity.IsAuthenticated)
{
bool jobDone = false;
int delay = 0;
int counter = 0;
do
{
delay = LockControllers.IsRequested(controller.User.Identity.Name);
if (delay == 0)
{
LockControllers.AddUser(controller.User.Identity.Name);
jobDone = true;
}
else
{
counter++;
System.Threading.Thread.Sleep(delay);
}
if (counter >= 10000)
{
context.HttpContext.Response.StatusCode = 400;
jobDone = true;
context.Result = new ContentResult()
{
Content = "Attack Detected"
};
}
} while (!jobDone);
}
}
catch (System.Exception)
{
}
}
public void OnActionExecuted(ActionExecutedContext context)
{
try
{
var controller = (Controller)context.Controller;
if (controller.User.Identity.IsAuthenticated)
{
LockControllers.RemoveUser(controller.User.Identity.Name);
}
}
catch (System.Exception)
{
}
}
I made list static list of user and sleep their thread until previous task happen.
Is there any better way to manage this problem?
So the original question has been edited so this answer is invalid.
so the issue isn't that the code runs too fast. Fast is always good :) The issue is that the account is going into negative funds. If the client decides to post a form twice that is the clients fault. It maybe that you only want the client to pay only once which is an other problem.
So for the first problem, I would recommend a using transactions (https://en.wikipedia.org/wiki/Database_transaction) to lock your table. Which means that the add update/add a change (or set of changes) and you force other calls to that table to wait until those operations have been done. You can always begin your transaction and check that the account has the correct amount of funds.
If the case is that they are only ever meant to pay once then.. then have a separate table that records if the user has payed (again within a transaction), before processing the update/add.
http://www.entityframeworktutorial.net/entityframework6/transaction-in-entity-framework.aspx
(Edit: fixing link)
You have a few options here
You implement ETag functionality in your app which you can use for optimistic concurrency. This works well, when you are working with records, i.e. you have a database with a data record, return that to the user and then the user changes it.
You could add an required field with a guid to your view model which you pass to your app and add it to in memory cache and check it on each request.
public class RegisterViewModel
{
[Required]
public Guid Id { get; set; }
/* other properties here */
...
}
and then use IMemoryCache or IDistributedMemoryCache (see ASP.NET Core Docs) to put this Id into the memory cache and validate it on request
public Task<IActioNResult> Register(RegisterViewModel register)
{
if(!ModelState.IsValid)
return BadRequest(ModelState);
var userId = ...; /* get userId */
if(_cache.TryGetValue($"Registration-{userId}", register.Id))
{
return BadRequest(new { ErrorMessage = "Command already recieved by this user" });
}
// Set cache options.
var cacheEntryOptions = new MemoryCacheEntryOptions()
// Keep in cache for 5 minutes, reset time if accessed.
.SetSlidingExpiration(TimeSpan.FromMinutes(5));
// when we're here, the command wasn't executed before, so we save the key in the cache
_cache.Set($"Registration-{userId}", register.Id, cacheEntryOptions );
// call your service here to process it
registrationService.Register(...);
}
When the second request arrives, the value will already be in the (distributed) memory cache and the operation will fail.
If the caller do not sets the Id, validation will fail.
Of course all that Jonathan Hickey listed in his answer below applies to, you should always validate that there is enough balance and use EF-Cores optimistic or pessimistic concurrency
I'm building a T4 template that will help people construct Azure queues in a consistent and simple manner. I'd like to make this self-documenting, and somewhat consistent.
First I made the queue name at the top of the file, the queue names have to be in lowercase so I added ToLower()
The public constructor uses the built-in StorageClient API's to access the connection strings. I've seen many different approaches to this, and would like to get something that works in almost all situations. (ideas? do share)
I dislike the unneeded HTTP requests to check if the queues have been created so I made is a static bool . I didn't implement a Lock(monitorObject) since I don't think one is needed.
Instead of using a string and parsing it with commas (like most MSDN documentation) I'm serializing the object when passing it into the queue.
For further optimization I'm using a JSON serializer extension method to get the most out of the 8k limit. Not sure if an encoding will help optimize this any more
Added retry logic to handle certain scenarios that occur with the queue (see html link)
Q: Is "DataContext" appropriate name for this class?
Q: Is it a poor practice to name the Queue Action Name in the manner I have done?
What additional changes do you think I should make?
public class AgentQueueDataContext
{
// Queue names must always be in lowercase
// Is named like a const, but isn't one because .ToLower won't compile...
static string AGENT_QUEUE_ACTION_NAME = "AgentQueueActions".ToLower();
static bool QueuesWereCreated { get; set; }
DataModel.SecretDataSource secDataSource = null;
CloudStorageAccount cloudStorageAccount = null;
CloudQueueClient cloudQueueClient = null;
CloudQueue queueAgentQueueActions = null;
static AgentQueueDataContext()
{
QueuesWereCreated = false;
}
public AgentQueueDataContext() : this(false)
{
}
public AgentQueueDataContext(bool CreateQueues)
{
// This pattern of setting up queues is from:
// ttp://convective.wordpress.com/2009/11/15/queues-azure-storage-client-v1-0/
//
this.cloudStorageAccount = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
this.cloudQueueClient = cloudStorageAccount.CreateCloudQueueClient();
this.secDataSource = new DataModel.SecretDataSource();
queueAgentQueueActions = cloudQueueClient.GetQueueReference(AGENT_QUEUE_ACTION_NAME);
if (QueuesWereCreated == false || CreateQueues)
{
queueAgentQueueActions.CreateIfNotExist();
QueuesWereCreated = true;
}
}
// This is the method that will be spawned using ThreadStart
public void CheckQueue()
{
while (true)
{
try
{
CloudQueueMessage msg = queueAgentQueueActions.GetMessage();
bool DoRetryDelayLogic = false;
if (msg != null)
{
// Deserialize using JSON (allows more data to be stored)
AgentQueueEntry actionableMessage = msg.AsString.FromJSONString<AgentQueueEntry>();
switch (actionableMessage.ActionType)
{
case AgentQueueActionEnum.EnrollNew:
{
// Add to
break;
}
case AgentQueueActionEnum.LinkToSite:
{
// Link within Agent itself
// Link within Site
break;
}
case AgentQueueActionEnum.DisableKey:
{
// Disable key in site
// Disable key in AgentTable (update modification time)
break;
}
default:
{
break;
}
}
//
// Only delete the message if the requested agent has been missing for
// at least 10 minutes
//
if (DoRetryDelayLogic)
{
if (msg.InsertionTime != null)
if (msg.InsertionTime < DateTime.UtcNow + new TimeSpan(0, 10, 10))
continue;
// ToDo: Log error: AgentID xxx has not been found in table for xxx minutes.
// It is likely the result of a the registratoin host crashing.
// Data is still consistent. Deleting queued message.
}
//
// If execution made it to this point, then we are either fully processed, or
// there is sufficent reason to discard the message.
//
try
{
queueAgentQueueActions.DeleteMessage(msg);
}
catch (StorageClientException ex)
{
// As of July 2010, this is the best way to detect this class of exception
// Description: ttp://blog.smarx.com/posts/deleting-windows-azure-queue-messages-handling-exceptions
if (ex.ExtendedErrorInformation.ErrorCode == "MessageNotFound")
{
// pop receipt must be invalid
// ignore or log (so we can tune the visibility timeout)
}
else
{
// not the error we were expecting
throw;
}
}
}
else
{
// allow control to fall to the bottom, where the sleep timer is...
}
}
catch (Exception e)
{
// Justification: Thread must not fail.
//Todo: Log this exception
// allow control to fall to the bottom, where the sleep timer is...
// Rationale: not doing so may cause queue thrashing on a specific corrupt entry
}
// todo: Thread.Sleep() is bad
// Replace with something better...
Thread.Sleep(9000);
}
Q: Is "DataContext" appropriate name for this class?
In .NET we have a lot of DataContext classes, so in the sense that you want names to appropriately communicate what the class does, I think XyzQueueDataContext properly communicates what the class does - although you can't query from it.
If you want to stay more aligned to accepted pattern languages, Patterns of Enterprise Application Architecture calls any class that encapsulates access to an external system for a Gateway, while more specifically you may want to use the term Channel in the language of Enterprise Integration Patterns - that's what I would do.
Q: Is it a poor practice to name the Queue Action Name in the manner I have done?
Well, it certainly tightly couples the queue name to the class. This means that if you later decide that you want to decouple those, you can't.
As a general comment I think this class might benefit from trying to do less. Using the queue is not the same thing as managing it, so instead of having all of that queue management code there, I'd suggest injecting a CloudQueue into the instance. Here's how I implement my AzureChannel constructor:
private readonly CloudQueue queue;
public AzureChannel(CloudQueue queue)
{
if (queue == null)
{
throw new ArgumentNullException("queue");
}
this.queue = queue;
}
This better fits the Single Responsibility Principle and you can now implement queue management in its own (reusable) class.