一次仅允许一个用户访问操作

时间:2019-07-09 14:30:01

标签: c# asp.net-core identityserver4 asp.net-core-webapi

我有与Web API通信的ASP.NET Core应用程序。 有一个涉及几个步骤的业务操作(在一页上执行,转到下一页和下一页)。这一多步骤操作是在一个元素的上下文中完成的。

因此,假设您有一个一些业务对象列表,而您的任务是从该列表中接受对象3 。接受是我们的多步骤操作,如果我当前正在接受对象3 ,则没有其他人可以输入对<对象> 3 的接受操作。完成操作后,应将其解锁。 希望这个问题是可以理解的。

我们不需要耗时的解决方案,最简单的想法是创建一个数据库表来指示用户何时开始操作,它保存对象的ID和用户的ID,例如在5分钟后自动删除自身,如果其他人想要访问操作,我们将检查该对象是否被阻止。但这有点骇人听闻,不是很干净(如果用户去喝咖啡并在10分钟后继续操作会怎样?)

我正在寻找一种更好的方式来实现这种行为并欣赏任何想法

2 个答案:

答案 0 :(得分:3)

如果要实现该行为,我还将使用数据库,但使用另一种方式。我将创建一个 objects 的表( object 3 是其行之一),为UserId,布尔值OnProcess添加一列(标记对象是否在处理中)和StartProcess的时间戳。

要使用户能够输入操作,请运行以下查询:

UPDATE Objects SET UserId = <CurrentUser>, StartProcess = <NOW>, OnProcess = true
OUTPUT Object.Id
WHERE Object.Id == 3 AND 
    (
        OnProcess == false
        OR ( OnProcess == true AND UserId == <CurrentUser> )
        OR ( StartProcess <is more than 15 minutes ago>)
    )

免责声明:上面的查询不是可执行查询,但是应该足够清楚地了解它的作用。

通过上述查询,在以下情况下将返回Object.Id

  • 该对象未被其他用户处理
  • CurrentUser本身正在处理该对象,同时也重置了StartProcess(某种滑动行为)。这样,如果CurrentUser AFK在给定时间(但不超过阈值时间)内又返回,则他/她可以轻松地继续操作
  • 在最近15分钟内未处理该对象。这实际上是我在前面提到的阈值。至于多长时间(在我的示例中为15分钟),实际上取决于您。

如果为用户返回了Object.Id,则他/她可以输入操作。

答案 1 :(得分:0)

您正在寻找信号灯。 lock关键字是最基本的信号量,但是您也可以使用Semaphore / SemaphoreSlim,它们提供了诸如速率限制之类的功能,而lock将一次只能执行一次操作。但是,您的目标是一次选择一个特定资源一次,这使SemaphoreSlim是一个更好的选择,特别是ConcurrentDictionary<string, SemaphoreSlim>

您将需要一个具有单例生存期的类(在应用程序的整个生命周期中一个实例)。在那里,您将添加一个ivar:

private readonly ConcurrentDictionary<string, SemaphoreSlim> _semaphores = new ConcurrentDictionary<string, SemaphoreSlim>();

然后,您将在要执行的操作周围添加以下代码:

var semaphore = _semaphores.GetOrAdd("object3", _ => new SemaphoreSlim(1, 1));
await semaphore.WaitAsync();

// do something

semaphore.Release();

"object3"显然只有一个占位符。您将想要使用任何有意义的东西(ID等)-可以唯一地标识您要控制的特定资源的东西。然后,仅当该特定资源上存在现有操作时,该操作才会保存该特定资源的操作。一种不同的资源将获得其自己的信号量,并因此获得其自己的大门。