public async Task <Page <XM.NotificationUser> > GetPageAsync(XM.NotificationSearchCriteria criteria)
        {
            if (criteria.ToUserId.HasValue)
            {
                return(await GetNotificationUserPageAsync(criteria));
            }

            // 备注:忽略搜索条件的 IsReaded, ToUserId
            // 备注:因为查询所有 ToUserId, 所有不会标记已读未读

            IQueryable <Notification> query = _tubumuContext.Notification;

            if (criteria.FromUserId.HasValue)
            {
                query = query.Where(m => m.FromUserId == criteria.FromUserId);
            }
            if (criteria.Keyword != null)
            {
                var keyword = criteria.Keyword.Trim();
                if (keyword.Length != 0)
                {
                    query = query.Where(m => m.Title.Contains(keyword));
                }
            }
            if (criteria.CreationDateBegin.HasValue)
            {
                var begin = criteria.CreationDateBegin.Value.Date;
                query = query.Where(m => m.CreationDate >= begin);
            }
            if (criteria.CreationDateEnd.HasValue)
            {
                var end = criteria.CreationDateEnd.Value.Date.AddDays(1);
                query = query.Where(m => m.CreationDate < end);
            }

            IOrderedQueryable <Notification> orderedQuery;

            if (criteria.PagingInfo.SortInfo != null && !criteria.PagingInfo.SortInfo.Sort.IsNullOrWhiteSpace())
            {
                orderedQuery = query.Order(criteria.PagingInfo.SortInfo.Sort, criteria.PagingInfo.SortInfo.SortDir == SortDir.DESC);
            }
            else
            {
                // 默认排序
                orderedQuery = query.OrderByDescending(m => m.NotificationId);
            }

            var page = await orderedQuery.Select(_notificationUserSelector).GetPageAsync(criteria.PagingInfo);

            return(page);
        }
        private async Task <Page <XM.NotificationUser> > GetNotificationUserPageAsync(XM.NotificationSearchCriteria criteria)
        {
            if (!criteria.ToUserId.HasValue)
            {
                throw new ArgumentNullException(nameof(criteria.ToUserId), "必须输入 ToUserId");
            }
            var userCreationDate = await _tubumuContext.User.AsNoTracking().Where(m => m.UserId == criteria.ToUserId.Value).Select(m => m.CreationDate).FirstOrDefaultAsync();

            // 备注:查询发送给所有人的以及本人的、未删除的记录
            var query1 = from n in _tubumuContext.Notification
                         where n.CreationDate > userCreationDate && (!n.ToUserId.HasValue || n.ToUserId == criteria.ToUserId.Value)
                         select n;

            if (criteria.FromUserId.HasValue)
            {
                query1 = query1.Where(m => m.FromUserId == criteria.FromUserId);
            }
            if (criteria.Keyword != null)
            {
                var keyword = criteria.Keyword.Trim();
                if (keyword.Length != 0)
                {
                    query1 = query1.Where(m => m.Title.Contains(keyword));
                }
            }
            if (criteria.CreationDateBegin.HasValue)
            {
                var begin = criteria.CreationDateBegin.Value.Date;
                query1 = query1.Where(m => m.CreationDate >= begin);
            }
            if (criteria.CreationDateEnd.HasValue)
            {
                var end = criteria.CreationDateEnd.Value.Date.AddDays(1);
                query1 = query1.Where(m => m.CreationDate < end);
            }

            // 剔除已逻辑删除的记录
            var query2 = from m in query1
                         from pu in m.NotificationUser.Where(n => n.UserId == criteria.ToUserId.Value).DefaultIfEmpty()
                         where pu == null || !pu.DeleteTime.HasValue
                         //join pu in m.NotificationUsers.Where(n=>n.UserId == criteria.ToUserId.Value) on m equals pu.Notification into purd
                         //from x in purd.DefaultIfEmpty()
                         //where x == null || !x.DeleteTime.HasValue
                         select new XM.NotificationUser
            {
                NotificationId = m.NotificationId,
                FromUser       = new XM.UserInfoWarpper
                {
                    UserId      = m.FromUserId.HasValue ? m.FromUser.UserId : 0,
                    Username    = m.FromUserId.HasValue ? m.FromUser.Username : "",
                    DisplayName = m.FromUserId.HasValue ? m.FromUser.DisplayName : "",
                    HeadUrl     = m.FromUserId.HasValue ? m.FromUser.HeadUrl : "",
                    LogoUrl     = m.FromUserId.HasValue ? m.FromUser.LogoUrl : "",
                },
                ToUser = new XM.UserInfoWarpper
                {
                    UserId      = m.ToUserId.HasValue ? m.ToUser.UserId : 0,
                    Username    = m.ToUserId.HasValue ? m.ToUser.Username : "",
                    DisplayName = m.ToUserId.HasValue ? m.ToUser.DisplayName : "",
                    HeadUrl     = m.ToUserId.HasValue ? m.ToUser.HeadUrl : "",
                    LogoUrl     = m.ToUserId.HasValue ? m.ToUser.LogoUrl : "",
                },
                Title        = m.Title,
                Message      = m.Message,
                Url          = m.Url,
                CreationDate = m.CreationDate,

                ReadTime   = pu != null ? pu.ReadTime : null,
                DeleteTime = pu != null ? pu.DeleteTime : null,
            };

            if (criteria.IsReaded.HasValue)
            {
                if (criteria.IsReaded.Value)
                {
                    // 备注,读取已读,也可通过用户的 NotificationsToUser 取
                    query2 = query2.Where(m => m.ReadTime.HasValue);
                }
                else
                {
                    query2 = query2.Where(m => !m.ReadTime.HasValue);
                }
            }

            IOrderedQueryable <XM.NotificationUser> orderedQuery;

            if (criteria.PagingInfo.SortInfo != null && !criteria.PagingInfo.SortInfo.Sort.IsNullOrWhiteSpace())
            {
                orderedQuery = query2.Order(criteria.PagingInfo.SortInfo.Sort, criteria.PagingInfo.SortInfo.SortDir == SortDir.DESC);
            }
            else
            {
                // 默认排序
                orderedQuery = query2.OrderByDescending(m => m.NotificationId);
            }

            var page = await orderedQuery.GetPageAsync(criteria.PagingInfo);

            return(page);
        }