【问题标题】:Get Unique Code using Singleton - ASP.NET Core使用 Singleton 获取唯一代码 - ASP.NET Core
【发布时间】:2018-04-19 21:35:12
【问题描述】:

我正在使用 ASP.NET Core 1.1 和 Entity Framework Core 1.1。

每当用户在系统中创建项目时,我都会尝试使用以下代码获取唯一的“项目代码”。这个案例即将处理并发/多个创建项目的请求。我在 ASP.NET 中使用“Singleton”类完成了类似的操作,但在 ASP.NET CORE 的情况下,“项目代码”有重复值(仅在少数情况下)。因此,只是想确保这里的代码对于为每个请求获取不同的“项目代码”是正确的。

-----------注入依赖---------------

namespace Microsoft.Extensions.DependencyInjection
{
   public static class StartupExtensions
   {
     public static IServiceCollection AddCloudscribeCore(this IServiceCollection services, IConfigurationRoot configuration)
    {
        // Add application services.            

        services.AddSingleton<ISingletonService, SingletonService>(); // this will create singleton instance of class.
        return services;
    }
  }
}

----- 单例服务-------

public class SingletonService : ISingletonService
{
    private readonly AppDbContext _context;

    public SingletonService(AppDbContext context)
    {
        _context = context;
    }


    public CommonResult GetUniqueCode(Guid tenantId)
    {
        CommonResult result = new CommonResult();
        string newCode = string.Empty;

        var query = (from tenant in _context.Tenants
                         join item in _context.InventoryItems on tenant.TenantId equals item.TenantId into tenantItemJoin
                         from subItem in tenantItemJoin.DefaultIfEmpty()
                         where tenant.TenantId == tenantId || subItem.ItemCode.Contains("I-")
                         orderby subItem.CreatedUtc descending
                         select new TenantItem
                         {
                             TenantCode = tenant.TenantCode,
                             ItemCode = (subItem != null ? (subItem.ItemCode == string.Empty ? string.Empty:subItem.ItemCode) : string.Empty)
                         }).FirstOrDefault();
        if (query  != null)
        {


            string tenantCode = query.TenantCode.Substring(2);
            if (!string.IsNullOrEmpty(query.ItemCode))
            {
                int code = Convert.ToInt32(query.ItemCode.Substring(2)) + 1;
                if (code < 10)
                    newCode = "I-00000" + code;
                else if (code >= 10 && code < 100)
                    newCode = "I-0000" + code;
                else if (code >= 100 && code < 1000)
                    newCode = "I-000" + code;
                else if (code >= 1000 && code < 10000)
                    newCode = "I-00" + code;
                else if (code >= 10000 && code < 100000)
                    newCode = "I-0" + code;
                else if (code >= 100000 && code < 1000000)
                    newCode = "I-" + code;
                else
                    newCode = "I-" + code;

            }
            else
            {
                newCode = "I-000001";
            }
            TenantItem tenantItem = new TenantItem();
            tenantItem.TenantCode = tenantCode;
            tenantItem.ItemCode = newCode;
            result.Succeeded = true;
            result.Object = tenantItem;

        }

        return result;
    }
}

------------- API 控制器 -------------

[HttpGet]
public CommonResult GetItemCode()
{
    CommonResult result = new CommonResult();
    result = _singletonService.GetItemCode(tenantId);
    TenantItem tenantItem = (TenantItem)result.Object;
}

如果我在数据库中看到输出/值,我会看到几个条目(其中数据输入时间差以毫秒为单位)具有重复的 ID。请参阅下面的摘录,

ItemCode CreatedUTC 
I-000045 2017-10-31 11:06:10.6419557
I-000045 2017-10-31 11:06:10.5482045
I-000044 2017-10-31 11:03:58.0772064
I-000043 2017-10-31 11:03:57.7803288
I-000042 2017-10-31 11:03:04.1054090
I-000042 2017-10-31 11:03:03.9632107
I-000032 2017-10-25 17:39:04.7054946
I-000031 2017-10-25 17:34:34.7300091 
I-000030 2017-10-25 17:24:15.8891966 
I-000029 2017-10-25 17:19:36.2187677 
I-000028 2017-10-25 17:06:19.8946515 
I-000027 2017-10-25 17:03:45.8059024 
I-000026 2017-10-25 11:48:59.2262869
I-000025 2017-10-25 11:47:15.5935031 
I-000024 2017-10-25 11:45:31.8251470 
I-000023 2017-10-25 11:43:55.6671755 
I-000022 2017-10-25 06:33:27.2546438 
I-000019 2017-10-24 11:27:05.2224204
I-000016 2017-10-24 10:21:09.5983741
I-000015 2017-10-24 10:18:54.7954042 
I-000011 2017-10-24 09:19:04.9033847
I-000010 2017-10-24 09:17:29.0905153
I-000008 2017-10-23 11:48:12.0188814
I-000007 2017-10-23 08:50:17.0785334 
I-000006 2017-10-23 08:46:36.6120703 
I-000005 2017-10-23 08:01:15.3829637

如果我在这里遗漏了什么,请提出建议。

【问题讨论】:

    标签: asp.net-core singleton


    【解决方案1】:

    这就是为什么最好让数据库处理标识样式列的原因。无论如何,这与单例等无关;这是简单的数据库并发。无论是否单身,您都在对数据库表运行查询以获取最新的“id”,将其递增并保存新记录。但是,除非您在表上发出锁(顺便说一句,这是个坏主意),否则可能会执行其他调用相同操作的请求。总而言之,您的代码不是线程安全的。

    使其线程安全的方法是 1) 创建锁或 2) 依赖唯一约束并采用捕获和重试策略。由于#1 几乎总是一个坏主意,尤其是在数据库场景中,#2 应该是您的首选路径。

    简单地说,如果这个值应该是唯一的,那么数据库中的列应该有一个唯一的约束。由于您能够实际创建重复项,显然情况并非如此。但是,如果您更正了这一点,那么尝试保存欺骗将导致数据库异常,该异常会冒泡并被您的应用程序捕获。然后,您可以捕获此异常并重试该操作,再次获取最新的“id”,再次递增,并再次尝试保存。冲洗并重复,直到它通过没有问题。

    这确实意味着该操作可能需要更长的时间才能完成,因为它可能需要多次往返数据库才能确定可用的“id”,但这是完成此操作的唯一安全方法,如果你拒绝使用身份列。

    【讨论】:

    • 谢谢,我会按照建议更改实现。
    【解决方案2】:

    1.您的数据库上下文不应嵌入到使用单例生命周期的服务中。除非您在注册 AppDbContext 时另行指定,否则它会设置为“作用域”生存期。我建议您还为包含数据库上下文的服务使用范围生命周期。

    2. 我会允许数据库为您的商品代码设置唯一 ID。它具有支持并发和确保唯一性的内置机制。在我看来,利用这些是有意义的。您可以在 InventoryItem 表上使用自动递增的 Id,并将其标记为主键或在列上添加唯一约束。

    参考文献:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-07-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多