// Models.ScanのScanRollsとUserをIncludeしておくことを忘れないように!
            public Value(Models.Scan scan)
            {
                if (scan == null)
                {
                    throw new ArgumentNullException(nameof(scan));
                }
                if (scan.ScanRolls == null)
                {
                    throw new ArgumentException(nameof(scan.ScanRolls));
                }

                WatchingExpr = scan.Expr ?? Expr.Main.Invalid;
                MaxSize      = scan.MaxSize;
                NoProgress   = scan.NoProgress;
                IsArchived   = scan.IsArchived;
                Ranking      =
                    scan.ScanRolls
                    .Where(r => r != null)
                    .OrderByDescending(r => r.Value)
                    .ThenByDescending(r => r.ValueTieBreaker)
                    .SelectMany((r, i) =>
                {
                    if (ulong.TryParse(r.UserID, out var userID))
                    {
                        return(new[] { (i, userID, r.User.Username, r.Value) });
        public async Task StartAsync(ulong channelId, ulong userId, string username, bool force, Expr.Main expr, int maxSize, bool noProgress)
        {
            if (force)
            {
                await EndAsync(channelId, userId, true);
            }
            using (var context = new MainDbContext(_config))
            {
                var scan =
                    await
                    context.Scans
                    .Where(s => !s.IsArchived)
                    .FirstOrDefaultAsync(s => s.ChannelID == channelId.ToString() && s.ScanStartedUserID == userId.ToString());

                if (scan == null)
                {
                    await AddOrUpdateUserAsync(context, userId, username);

                    var newScan =
                        new Models.Scan
                    {
                        ChannelID         = channelId.ToString(),
                        ScanStartedUserID = userId.ToString(),
                        StartedAt         = _config.GetUtcNow(),
                        Expr       = expr,
                        MaxSize    = maxSize,
                        NoProgress = noProgress,
                    };
                    await context.Scans.AddAsync(newScan);
                }
                else
                {
                    await RespondByCautionAsync("すでに別の集計が行われています。", channelId, userId);

                    return;
                }
                await context.SaveChangesAsync();
            }

            await RespondBySayAsync($"{expr.ToString()} の集計が開始されました。", channelId, userId);
        }