Home > Blockchain >  C#: do not let the action run more than once at a time
C#: do not let the action run more than once at a time

Time:11-21

I have a backend system that uses .net 6 where I generate user names by manually auto increase.

Our user names are company and role based. So if we assume that we have the companies a1 and a2 and the roles employee and manager, we will have ids in the {company}-{role}{number} like a1-e0001, a2-m0010, ...

The code is something like this:

[HttpPost]
[Route("create")]
public async Task<BaseDto?> Create([FromBody] reqDto)
{
    var lastUser = usersRepository
    .Where(e => e.Company == reqDto.Company && e.Role == reqDto.Role)
    .SortByUserName().LastOrDefault();

    var lastUserName = lastUser?.UserName;
    var lastUserNameInt = int.Parse(lastUserName.Split('-')[1]);
    var newUserName = e.Company   "-"   e.Role   (lastUserNameInt   1);

    // create the user
    // ...
    // ...

The problem is when this method gets called more than once at a short duration, which is a realistic case, it will collide and give the same username to the 2 requests, as the second request will start running before the first one inserts the new user, so the second request also will get the same username.

What is a good way to avoid such collisions? I have thought of blocking the other calls to this question until the previous call resolves, but could not find a way how to do this.

CodePudding user response:

Blocking will work but will greatly decrease performance in your case. My solution would be to use some sort of db level manageable identity seed. For e.g. in MSSQL you can create table that will generate ID for you on insert like

CREATE TABLE [dbo].[User](
    [Id] [int] IDENTITY(1,1) NOT NULL
 CONSTRAINT [PK_Marker] PRIMARY KEY CLUSTERED 
(
    [Id] ASC )

on insert you'll have thread safe ID and you can present it to your user later by simple concat $"u-{ID}". This way you don't need to worry about concurrent access and you do have better login than with guid. I'm sure you don't need to stuck with MSSQL and your db engine can do this too.

CodePudding user response:

You can use Interlocked.Increment, which is thread safe and its return value will guarantee you'll get the incremented value back to use as id.

var current = 5;
var next = Interlocked.Increment(ref current);
Console.WriteLine(next); //prints 6

As note; This will require you to only have one instance of the app running.

CodePudding user response:

am i missing something or wouldn't lock(object) be something fitting for you?

  • Related