private async void OnSessionBegin() { // 更新已分配机器人的 SessionId try { var allocatedBots = await Client.GetAllocatedBots(); if (allocatedBots != null) { using (var dbContext = new KeylolDbContext()) { var bots = await dbContext.SteamBots.Where(b => allocatedBots.Contains(b.Id)).ToListAsync(); foreach (var bot in bots) { if (Sessions.ContainsKey(bot.SessionId)) { await Sessions[bot.SessionId].Client.StopBot(bot.Id); } bot.SessionId = SessionId; } await dbContext.SaveChangesAsync(KeylolDbContext.ConcurrencyStrategy.ClientWin); } } } catch (Exception) { // ignored } }
/// <summary> /// 当用户与机器人不再为好友时,通过此方法通知协调器 /// </summary> /// <param name="userSteamId">用户 Steam ID</param> /// <param name="botId">机器人 ID</param> public async Task OnUserBotRelationshipNone(string userSteamId, string botId) { using (var dbContext = new KeylolDbContext()) { var userManager = new KeylolUserManager(dbContext); var user = await userManager.FindBySteamIdAsync(userSteamId); if (user == null) { // 非会员不再为好友时,如果存在已绑定的 SteamBindingToken 则清除之 var bindingTokens = await dbContext.SteamBindingTokens .Where(t => t.SteamId == userSteamId && t.BotId == botId) .ToListAsync(); dbContext.SteamBindingTokens.RemoveRange(bindingTokens); await dbContext.SaveChangesAsync(KeylolDbContext.ConcurrencyStrategy.DatabaseWin); } else if (user.SteamBotId == botId) { // 会员与自己的机器人不再为好友时,解除机器人绑定 user.SteamBotId = null; await dbContext.SaveChangesAsync(KeylolDbContext.ConcurrencyStrategy.DatabaseWin); } } }
/// <summary> /// 创建 <see cref="ActivityCommentController"/> /// </summary> /// <param name="dbContext"><see cref="KeylolDbContext"/></param> /// <param name="cachedData"><see cref="CachedDataProvider"/></param> /// <param name="userManager"><see cref="KeylolUserManager"/></param> public ActivityCommentController(KeylolDbContext dbContext, CachedDataProvider cachedData, KeylolUserManager userManager) { _dbContext = dbContext; _cachedData = cachedData; _userManager = userManager; }
/// <summary> /// 更新指定机器人的属性 /// </summary> /// <param name="id">要更新的机器人 ID</param> /// <param name="friendCount">好友数,<c>null</c> 表示不更新</param> /// <param name="online">是否在线,<c>null</c> 表示不更新</param> /// <param name="steamId">Steam ID,<c>null</c> 表示不更新</param> public async Task UpdateBot(string id, int?friendCount = null, bool?online = null, string steamId = null) { using (var dbContext = new KeylolDbContext()) { var bot = await dbContext.SteamBots.FindAsync(id); if (bot == null) { return; } if (friendCount != null) { bot.FriendCount = friendCount.Value; } if (online != null) { bot.Online = online.Value; } if (steamId != null) { bot.SteamId = steamId; } await dbContext.SaveChangesAsync(KeylolDbContext.ConcurrencyStrategy.DatabaseWin); } }
/// <summary> /// 重新计算每个客户端应该分配的机器人数量并通知客户端 /// </summary> /// <param name="newSession">新 Session,在分配机器人时会最后分配</param> private static async Task ReallocateBots(SteamBotCoordinator newSession) { try { using (var dbContext = new KeylolDbContext()) { var botCount = await dbContext.SteamBots.CountAsync(b => b.Enabled); var averageCount = botCount; var lastCount = botCount; Func <List <string>, KeylolDbContext, Task> prepareDeallocatedBots = async(botIds, db) => { var deallocatedBots = await db.SteamBots.Where(b => botIds.Contains(b.Id)).ToListAsync(); foreach (var bot in deallocatedBots) { bot.SessionId = null; } await db.SaveChangesAsync(KeylolDbContext.ConcurrencyStrategy.ClientWin); }; foreach (var session in Sessions.Values.Where(s => s != newSession)) { await prepareDeallocatedBots(await session.Client.RequestReallocateBots(averageCount), dbContext); } await prepareDeallocatedBots(await newSession.Client.RequestReallocateBots(lastCount), dbContext); } } catch (Exception) { // ignored } }
/// <summary> /// 创建 <see cref="DiscoveryPage"/> /// </summary> /// <param name="currentUserId">当前登录用户 ID</param> /// <param name="dbContext"><see cref="KeylolDbContext"/></param> /// <param name="cachedData"><see cref="CachedDataProvider"/></param> /// <returns><see cref="DiscoveryPage"/></returns> public static async Task <DiscoveryPage> CreateAsync(string currentUserId, KeylolDbContext dbContext, CachedDataProvider cachedData) { var onSalePoints = await OnSalePointList.CreateAsync(currentUserId, 1, true, true, dbContext, cachedData); var latestArticles = await LatestArticleList.CreateAsync(1, true, true, dbContext, cachedData); return(new DiscoveryPage { SlideshowEntries = await SlideshowEntryList.CreateAsync(1, 4, dbContext), SpotlightPoints = await SpotlightPointList.CreateAsync(currentUserId, 1, 30, dbContext, cachedData), SpotlightReviews = await SpotlightArticleList.CreateAsync(currentUserId, 1, 4, SpotlightArticleStream.ArticleCategory.Review, dbContext, cachedData), SpotlightStudies = await SpotlightArticleList.CreateAsync(currentUserId, 1, 4, SpotlightArticleStream.ArticleCategory.Study, dbContext, cachedData), OnSalePointHeaderImage = onSalePoints.Item3, OnSalePointPageCount = onSalePoints.Item2, OnSalePoints = onSalePoints.Item1, SpotlightStories = await SpotlightArticleList.CreateAsync(currentUserId, 1, 4, SpotlightArticleStream.ArticleCategory.Story, dbContext, cachedData), LatestArticleHeaderImage = latestArticles.Item3, LatestArticlePageCount = latestArticles.Item2, LatestArticles = latestArticles.Item1, LatestActivityHeaderImage = onSalePoints.Item4, LatestActivities = await TimelineCardList.CreateAsync(LatestActivityStream.Name, currentUserId, 12, false, dbContext, cachedData) }); }
/// <summary> /// Called when the connection connects to this hub instance. /// </summary> /// <returns>A <see cref="T:System.Threading.Tasks.Task" /></returns> public override async Task OnConnected() { using (var dbContext = new KeylolDbContext()) { var bot = await GetNextBotAsync(dbContext); var random = new Random(); string code; do { var sb = new StringBuilder(); for (var i = 0; i < 4; i++) { sb.Append((char)random.Next('A', 'Z')); } sb.Append(random.Next(0, 10000).ToString("D4")); code = sb.ToString(); } while (dbContext.SteamBindingTokens.Any(t => t.Code == code)); var token = new SteamBindingToken { Code = code, BrowserConnectionId = Context.ConnectionId, BotId = bot?.Id }; dbContext.SteamBindingTokens.Add(token); await dbContext.SaveChangesAsync(); Clients.Caller.OnCode(token.Id, code, bot?.SteamId, $"其乐机器人 #{bot?.Sid}"); } }
/// <summary> /// 获取用户个人层级状态树 /// </summary> /// <param name="entrance">要获取的页面</param> /// <param name="userIdCode">用户识别码</param> /// <param name="dbContext"><see cref="KeylolDbContext"/></param> /// <param name="cachedData"><see cref="CachedDataProvider"/></param> /// <param name="userManager"><see cref="KeylolUserManager"/></param> /// <returns><see cref="UserLevel"/></returns> public static async Task <UserLevel> Get(string entrance, string userIdCode, [Injected] KeylolDbContext dbContext, [Injected] CachedDataProvider cachedData, [Injected] KeylolUserManager userManager) { return(await CreateAsync(StateTreeHelper.GetCurrentUserId(), userIdCode, entrance.ToEnum <EntrancePage>(), dbContext, cachedData, userManager)); }
/// <summary> /// 获取推送参考 /// </summary> /// <param name="link">文章链接</param> /// <param name="dbContext"><see cref="KeylolDbContext"/></param> /// <returns><see cref="SpotlightArticle"/></returns> public static async Task <SpotlightArticle> GetReference(string link, [Injected] KeylolDbContext dbContext) { var result = new SpotlightArticle(); var match = Regex.Match(link, @"^https?:\/\/.+\.keylol\.com(?::\d+)?\/article\/(.+)\/(\d+)$"); if (!match.Success) { return(result); } var idCode = match.Groups[1].Value; var sidForAuthor = int.Parse(match.Groups[2].Value); var article = await dbContext.Articles .Where(a => a.Author.IdCode == idCode && a.SidForAuthor == sidForAuthor) .Select(a => new { a.Id, a.Title, a.Subtitle, AuthorIdCode = a.Author.IdCode, PointIdCode = a.TargetPoint.IdCode }) .SingleOrDefaultAsync(); if (article == null) { return(result); } result.Id = article.Id; result.Title = article.Title; result.Subtitle = article.Subtitle; result.AuthorIdCode = article.AuthorIdCode; result.PointIdCode = article.PointIdCode; return(result); }
/// <summary> /// 获取推送参考 /// </summary> /// <param name="link">文章链接</param> /// <param name="dbContext"><see cref="KeylolDbContext"/></param> /// <returns><see cref="SlideshowEntry"/></returns> public static async Task <SlideshowEntry> GetReference(string link, [Injected] KeylolDbContext dbContext) { var result = new SlideshowEntry(); var match = Regex.Match(link, @"^https?:\/\/.+\.keylol\.com(?::\d+)?\/article\/(.+)\/(\d+)$"); if (!match.Success) { return(result); } var idCode = match.Groups[1].Value; var sidForAuthor = int.Parse(match.Groups[2].Value); var article = await dbContext.Articles .Where(a => a.Author.IdCode == idCode && a.SidForAuthor == sidForAuthor) .Select(a => new { a.Title, a.Subtitle, AuthorUserName = a.Author.UserName, a.PublishTime, a.CoverImage }) .SingleOrDefaultAsync(); if (article == null) { return(result); } result.Title = article.Title; result.Subtitle = article.Subtitle; result.Author = article.AuthorUserName; result.Date = article.PublishTime.Date.ToString("M月d日"); result.BackgroundImage = article.CoverImage; return(result); }
/// <summary> /// 创建 <see cref="RelatedPointList"/> /// </summary> /// <param name="pointId">据点 ID</param> /// <param name="dbContext"><see cref="KeylolDbContext"/></param> /// <returns><see cref="RelatedPointList"/></returns> public static async Task <RelatedPointList> CreateAsync(string pointId, KeylolDbContext dbContext) { return(new RelatedPointList((await(from relationship in dbContext.PointRelationships where relationship.SourcePointId == pointId && (relationship.Relationship == PointRelationshipType.Developer || relationship.Relationship == PointRelationshipType.Manufacturer || relationship.Relationship == PointRelationshipType.Series || relationship.Relationship == PointRelationshipType.Tag) select new { relationship.TargetPoint.Id, relationship.TargetPoint.Type, relationship.TargetPoint.AvatarImage, relationship.TargetPoint.ChineseName, relationship.TargetPoint.EnglishName }).Distinct().Take(10).ToListAsync()) .Select(p => new PointBasicInfo { Id = p.Id, Type = p.Type, AvatarImage = p.AvatarImage, ChineseName = p.ChineseName, EnglishName = p.EnglishName }))); }
/// <summary> /// 创建 <see cref="DossierPage"/> /// </summary> /// <param name="user">用户对象</param> /// <param name="currentUserId">当前登录用户 ID</param> /// <param name="dbContext"><see cref="KeylolDbContext"/></param> /// <param name="cachedData"><see cref="CachedDataProvider"/></param> /// <param name="userManager"><see cref="KeylolUserManager"/></param> /// <returns><see cref="DossierPage"/></returns> public static async Task <DossierPage> CreateAsync(KeylolUser user, string currentUserId, KeylolDbContext dbContext, CachedDataProvider cachedData, KeylolUserManager userManager) { var subscribedPoints = await SubscribedPointList.CreateAsync(currentUserId, user.Id, 1, 3, true, dbContext, cachedData); var selectedArticles = await SelectedArticleList.CreateAsync(user.Id, 1, 8, true, currentUserId, dbContext, cachedData); var dossierPage = new DossierPage { Coupon = user.Coupon, LikeCount = await cachedData.Likes.GetUserLikeCountAsync(user.Id), GameCount = await dbContext.UserSteamGameRecords.CountAsync(r => r.UserId == user.Id), PlayedGameCount = await dbContext.UserSteamGameRecords.CountAsync(r => r.UserId == user.Id && r.TotalPlayedTime > 0), SpotlightCount = await dbContext.Articles.CountAsync(a => a.AuthorId == user.Id && a.Spotlighted), IsOperator = await userManager.IsInRoleAsync(user.Id, KeylolRoles.Operator), SubscribedPointCount = subscribedPoints.Item2, SubscribedPoints = subscribedPoints.Item1, ArticleCount = selectedArticles.Item2, SelectedArticles = selectedArticles.Item1 }; return(dossierPage); }
/// <summary> /// 获取一条动态 /// </summary> /// <param name="authorIdCode">作者识别码</param> /// <param name="sidForAuthor">动态在作者名下的序号</param> /// <param name="dbContext"><see cref="KeylolDbContext"/></param> /// <param name="cachedData"><see cref="CachedDataProvider"/></param> /// <param name="userManager"><see cref="KeylolUserManager"/></param> /// <returns><see cref="ActivityPage"/></returns> public static async Task <ActivityPage> Get(string authorIdCode, int sidForAuthor, [Injected] KeylolDbContext dbContext, [Injected] CachedDataProvider cachedData, [Injected] KeylolUserManager userManager) { return(await CreateAsync(authorIdCode, sidForAuthor, StateTreeHelper.GetCurrentUserId(), StateTreeHelper.GetCurrentUser().IsInRole(KeylolRoles.Operator), dbContext, cachedData, userManager)); }
/// <summary> /// 创建 <see cref="SubscriptionController"/> /// </summary> /// <param name="cachedData"><see cref="CachedDataProvider"/></param> /// <param name="dbContext"><see cref="KeylolDbContext"/></param> /// <param name="userManager"><see cref="KeylolUserManager"/></param> public SubscriptionController(CachedDataProvider cachedData, KeylolDbContext dbContext, KeylolUserManager userManager) { _cachedData = cachedData; _dbContext = dbContext; _userManager = userManager; }
/// <summary> /// 更新指定文章 /// </summary> /// <param name="id">文章 ID</param> /// <param name="content">新的内容</param> /// <param name="coverImage">新的封面图</param> /// <param name="rowVersion">参考 RowVersion</param> public async Task UpdateArticle(string id, string content = null, string coverImage = null, byte[] rowVersion = null) { using (var dbContext = new KeylolDbContext()) { var article = await dbContext.Articles.FindAsync(id); if (article == null) { throw new FaultException("文章不存在"); } if (rowVersion != null && !article.RowVersion.SequenceEqual(rowVersion)) { throw new FaultException("检测到文章已被编辑过,更新失败"); } if (content != null) { article.Content = content; } if (coverImage != null) { article.CoverImage = coverImage; } await dbContext.SaveChangesAsync(KeylolDbContext.ConcurrencyStrategy.DatabaseWin); } }
/// <summary> /// 创建 <see cref="RankingPage"/> /// </summary> /// <param name="currentUserId">当前登录用户 ID</param> /// <param name="dbContext"><see cref="KeylolDbContext"/></param> /// <param name="cachedData"><see cref="CachedDataProvider"/></param> /// <returns><see cref="RankingPage"/></returns> public static async Task <RankingPage> CreateAsync(string currentUserId, KeylolDbContext dbContext, CachedDataProvider cachedData) { return(new RankingPage { RankingUsers = await RankingUserList.CreateAsync(currentUserId, 1, dbContext, cachedData) }); }
/// <summary> /// 创建 <see cref="ProductPage"/> /// </summary> /// <param name="point">已经查询好的据点对象</param> /// <param name="currentUserId">当前登录用户 ID</param> /// <param name="dbContext"><see cref="KeylolDbContext"/></param> /// <param name="cachedData"><see cref="CachedDataProvider"/></param> /// <returns><see cref="ProductPage"/></returns> public static async Task <ProductPage> CreateAsync(Models.Point point, string currentUserId, KeylolDbContext dbContext, CachedDataProvider cachedData) { return(new ProductPage { Products = await ProductPointList.CreateAsync(currentUserId, point.Id, dbContext, cachedData) }); }
/// <summary> /// 创建 <see cref="ArticleCommentController" /> /// </summary> /// <param name="dbContext"> /// <see cref="KeylolDbContext" /> /// </param> /// <param name="userManager"> /// <see cref="KeylolUserManager" /> /// </param> /// <param name="cachedData"><see cref="CachedDataProvider"/></param> /// <param name="mqChannel"><see cref="IModel"/></param> public ArticleCommentController(KeylolDbContext dbContext, KeylolUserManager userManager, CachedDataProvider cachedData, IModel mqChannel) { _dbContext = dbContext; _userManager = userManager; _cachedData = cachedData; _mqChannel = mqChannel; }
/// <summary> /// 创建 <see cref="ActivityController"/> /// </summary> /// <param name="dbContext"><see cref="KeylolDbContext"/></param> /// <param name="mqChannel"><see cref="IModel"/></param> /// <param name="userManager"><see cref="KeylolUserManager"/></param> /// <param name="cachedData"><see cref="CachedDataProvider"/></param> public ActivityController(KeylolDbContext dbContext, IModel mqChannel, KeylolUserManager userManager, CachedDataProvider cachedData) { _dbContext = dbContext; _mqChannel = mqChannel; _userManager = userManager; _cachedData = cachedData; }
/// <summary> /// 创建 <see cref="ArticleResultList"/> /// </summary> /// <param name="keyword">搜索关键字</param> /// <param name="dbContext"><see cref="KeylolDbContext"/></param> /// <param name="cachedData"><see cref="CachedDataProvider"/></param> /// <param name="searchAll">是否查询全部</param> public static async Task <ArticlePage> CreateAsync(string keyword, [Injected] KeylolDbContext dbContext, [Injected] CachedDataProvider cachedData, bool searchAll = true) { return(new ArticlePage { Results = await ArticleResultList.CreateAsync(keyword, dbContext, cachedData, 1, searchAll) }); }
/// <summary> /// 创建 <see cref="StorePage"/> /// </summary> /// <param name="currentUserId">当前登录用户 ID</param> /// <param name="dbContext"><see cref="KeylolDbContext"/></param> /// <param name="cachedData"><see cref="CachedDataProvider"/></param> /// <param name="userManager"><see cref="KeylolUserManager"/></param> /// <param name="coupon"></param> /// <returns><see cref="StorePage"/></returns> public static async Task <StorePage> CreateAsync(string currentUserId, [Injected] KeylolDbContext dbContext, [Injected] CachedDataProvider cachedData, [Injected] KeylolUserManager userManager, [Injected] CouponProvider coupon) { return(new StorePage { Gifts = await CouponGiftList.CreateAsync(currentUserId, dbContext, cachedData, userManager, coupon) }); }
/// <summary> /// 获取时间轴卡片列表 /// </summary> /// <param name="before">起始位置</param> /// <param name="take">获取数量</param> /// <param name="dbContext"><see cref="KeylolDbContext"/></param> /// <param name="cachedData"><see cref="CachedDataProvider"/></param> /// <returns><see cref="TimelineCardList"/></returns> public static async Task <TimelineCardList> GetCards(int before, int take, [Injected] KeylolDbContext dbContext, [Injected] CachedDataProvider cachedData) { var currentUserId = StateTreeHelper.GetCurrentUserId(); return(await TimelineCardList.CreateAsync(SubscriptionStream.Name(currentUserId), currentUserId, take, false, dbContext, cachedData, before)); }
/// <summary> /// 创建 <see cref="UserPage"/> /// </summary> /// <param name="currentUserId">当前登录用户 ID</param> /// <param name="keyword">搜索关键字</param> /// <param name="dbContext"><see cref="KeylolDbContext"/></param> /// <param name="cachedData"><see cref="CachedDataProvider"/></param> /// <param name="searchAll">是否全部查询</param> /// <returns></returns> public static async Task <UserPage> CreateAsync(string currentUserId, string keyword, [Injected] KeylolDbContext dbContext, [Injected] CachedDataProvider cachedData, bool searchAll = true) { return(new UserPage { Results = await UserResultList.CreateAsync(currentUserId, keyword, dbContext, cachedData, 1, searchAll) }); }
/// <summary> /// 创建 <see cref="TimelinePage"/> /// </summary> /// <param name="currentUserId">当前登录用户 ID</param> /// <param name="dbContext"><see cref="KeylolDbContext"/></param> /// <param name="cachedData"><see cref="CachedDataProvider"/></param> /// <returns><see cref="TimelinePage"/></returns> public static async Task <TimelinePage> CreateAsync(string currentUserId, KeylolDbContext dbContext, CachedDataProvider cachedData) { return(new TimelinePage { Cards = await TimelineCardList.CreateAsync(SubscriptionStream.Name(currentUserId), currentUserId, 18, false, dbContext, cachedData) }); }
/// <summary> /// 创建 <see cref="LikeController" /> /// </summary> /// <param name="coupon"> /// <see cref="CouponProvider" /> /// </param> /// <param name="dbContext"> /// <see cref="KeylolDbContext" /> /// </param> /// <param name="userManager"> /// <see cref="KeylolUserManager" /> /// </param> /// <param name="cachedData"> /// <see cref="CachedDataProvider"/> /// </param> /// <param name="mqChannel"><see cref="IModel"/></param> public LikeController(CouponProvider coupon, KeylolDbContext dbContext, KeylolUserManager userManager, CachedDataProvider cachedData, IModel mqChannel) { _coupon = coupon; _dbContext = dbContext; _userManager = userManager; _cachedData = cachedData; _mqChannel = mqChannel; }
/// <summary> /// 创建 <see cref="ArticleController" /> /// </summary> /// <param name="mqChannel"> /// <see cref="IModel" /> /// </param> /// <param name="coupon"> /// <see cref="CouponProvider" /> /// </param> /// <param name="dbContext"> /// <see cref="KeylolDbContext" /> /// </param> /// <param name="userManager"> /// <see cref="KeylolUserManager" /> /// </param> /// <param name="cachedData"><see cref="CachedDataProvider"/></param> public ArticleController(IModel mqChannel, CouponProvider coupon, KeylolDbContext dbContext, KeylolUserManager userManager, CachedDataProvider cachedData) { _mqChannel = mqChannel; _coupon = coupon; _dbContext = dbContext; _userManager = userManager; _cachedData = cachedData; }
/// <summary> /// 判断指定 Steam 账户是不是其乐用户并且匹配指定机器人 /// </summary> /// <param name="steamId">Steam ID</param> /// <param name="botId">机器人 ID</param> /// <returns><c>true</c> 表示是其乐用户并于目标机器人匹配,<c>false</c> 表示不是</returns> public async Task <bool> IsKeylolUser(string steamId, string botId) { using (var dbContext = new KeylolDbContext()) { var userManager = new KeylolUserManager(dbContext); var user = await userManager.FindBySteamIdAsync(steamId); return(user != null && user.SteamBotId == botId); } }
/// <summary> /// 获取信息页 /// </summary> /// <param name="pointIdCode">据点识别码</param> /// <param name="dbContext"><see cref="KeylolDbContext"/></param> /// <returns><see cref="InfoPage"/></returns> public static async Task <InfoPage> Get(string pointIdCode, [Injected] KeylolDbContext dbContext) { var point = await dbContext.Points.Where(p => p.IdCode == pointIdCode).SingleOrDefaultAsync(); if (point == null) { return(new InfoPage()); } return(await CreateAsync(point, dbContext)); }
/// <summary> /// 创建 <see cref="UserController" /> /// </summary> /// <param name="coupon"> /// <see cref="CouponProvider" /> /// </param> /// <param name="owinContextProvider"> /// <see cref="OwinContextProvider" /> /// </param> /// <param name="dbContext"> /// <see cref="KeylolDbContext" /> /// </param> /// <param name="userManager"> /// <see cref="KeylolUserManager" /> /// </param> /// <param name="oneTimeToken"> /// <see cref="OneTimeTokenProvider" /> /// </param> /// <param name="roleManager"><see cref="KeylolRoleManager"/></param> public UserController(CouponProvider coupon, OwinContextProvider owinContextProvider, KeylolDbContext dbContext, KeylolUserManager userManager, OneTimeTokenProvider oneTimeToken, KeylolRoleManager roleManager) { _coupon = coupon; _owinContext = owinContextProvider.Current; _dbContext = dbContext; _userManager = userManager; _oneTimeToken = oneTimeToken; _roleManager = roleManager; }
/// <summary> /// 创建新 <see cref="CachedDataProvider" /> /// </summary> /// <param name="dbContext"> /// <see cref="KeylolDbContext" /> /// </param> /// <param name="redis"> /// <see cref="RedisProvider" /> /// </param> public CachedDataProvider(KeylolDbContext dbContext, RedisProvider redis) { Likes = new LikeOperations(dbContext, redis); Subscriptions = new SubscriptionOperations(dbContext, redis); Points = new PointOperations(dbContext, redis); Users = new UserOperations(dbContext, redis, Subscriptions); ArticleComments = new ArticleCommentOperations(dbContext, redis); ActivityComments = new ActivityCommentOperations(dbContext, redis); Messages = new MessageOperations(dbContext, redis); }