嵌套的foreach - 逻辑问题

时间:2015-08-03 23:22:32

标签: asp.net-mvc foreach controller logic

这让我在过去的3个小时里难过......我可能只是累了但似乎无法让逻辑正确。我想做的是;

  1. 获取调查主题列表和评级调查主题列表。
  2. 如果没有评论任何主题,请将第一个主题返回给用户进行评分。
  3. 如果他们都已被评分,请返回一个视图,说明您已完成调查'
  4. 否则确定哪些尚未评级,并在视图中为其提供服务。
  5. 所有主题一次提供1个,每次主题评级,保存后我将其重定向回该控制器。

    我认为我的string.equals不起作用,但似乎无法找出原因。控制器只是继续提供相同的主题。 (我假设它的第一个记录与那个不匹配的记录相匹配?)

     public ActionResult Index(string page)
        {
            Rating rating = new Rating();
    
            var surveyItems = (from s in db.Surveys
                               where s.Category.Name.Equals(page)
                               select s).ToList();
    
            var ratedItems = (from r in db.Ratings
                              where r.Category.Equals(page) && r.UserName.Equals(User.Identity.Name)
                              select r).ToList();
    
            if (ratedItems.Count() == 0 && surveyItems.Count() > 0)
            {
                ViewBag.Remaining = surveyItems.Count(); 
                rating.Topic = surveyItems.Select(si => si.Topic).FirstOrDefault();
                rating.Category = page;
                return View(rating);
            }
            else if (ratedItems.Count() > 0 && ratedItems.Count() == surveyItems.Count())
            {
                return View("Finished");
            }
            else
            {
                foreach (var si in surveyItems)
                {
                    foreach (var ri in ratedItems)
                    {
                        if (!si.Topic.Equals(ri.Topic))
                        {
    
                        rating.Topic = si.Topic;
                        rating.Category = page;
                        ViewBag.Total = surveyItems.Count();
                        ViewBag.Remaining = ViewBag.Total - ratedItems.Count();
                        return View(rating);
    
                        }
                    }
                }
    
            }
    

2 个答案:

答案 0 :(得分:1)

首先,要直接回答您的问题,您的内部循环将始终失败,因为2个列表未被排序 AND 并且没有保证每个列表中的项目1将是相同的。即使它们是,第一个列表中的第二个项目也不会等于第二个列表中的第一个项目(内部循环)。

最好的办法是完全使用LINQ解决这个问题,虽然查询有点难以理解,但代码更清晰。

var rating = (from s in db.Surveys
    join r in db.Ratingson s.Topic equals r.Topic into rated
    from ri in rated.Where(x => x.Username == User.Identity.Name).DefaultIfEmpty()
    where s.Category.Name.Equals(page) && ri.Topic == null
    select new RatingViewModel {Topic = s.Topic, Category = s.Category, Total = db.SurveyItems.Count(), Rated = rated.Count()}).FirstOrDefault();

if (rating == null)
{
    return View("Finished");
}

return View(rating);

LINQ查询本质上等同于以下SQL(给出或接受)

SELECT * FROM Surveys s
LEFT OUTER JOIN Ratings r ON s.Topic = r.Topic AND r.Username = 'user'
WHERE r.Topic IS NULL

您还会注意到查询投放到RatingsViewModel,我添加了这个,因为我注意到您对ViewBag以及Rating实体有一些引用。< / p>

RatingViewModel:

public class RatingViewModel
{
    public string Topic { get; set; }
    public string Category { get; set; }
    public int Total { get; set; }
    public int Rated { get; set; }
    public int Remaining {
        get { return Total - Rated; }
    }

}

修改

更多地使用查询,这是我能得到的最接近的:

// define the where's here so we can use the IQueryable multiple times in LINQ 
var surveys = db.Surveys.Where(x => x.Category.Name.Equals(page));
var ratedItems = db.Ratings.Where(y => y.Username == User.Identity.Name && y.Category.Name.Equals(page));
var rated = ratedItems.Count(); // get the rated count here, otherwise we end up with an exception inside the LINQ query

var surveyTopic =
(from s in surveys

    // LEFT OUTER JOIN to ratings
    join r in ratedItems on s.Topic equals r.Topic into ratedSurveys
    from ri in ratedSurveys.DefaultIfEmpty()

    // define variables
    let total = surveys.Count()
    //let rated = ratedItems.Count()  -- this throws a NotSupportedException... which seems odd considering the line above

    // get non-rated surveys, in this case the RIGHT side of the join (Ratings) is null if there is no rating
    where ri.Topic == null

    // projection
    select new RatingViewModel
    {
        Topic = s.Topic,
        Category = s.Category,
        Rated = rated,
        Total = total
    }).FirstOrDefault();

return surveyTopic == null ? View("Finished") : View(surveyTopic);

不幸的是,这导致我希望避免的2个DB查询,但这应该更接近你所追求的。

答案 1 :(得分:0)

布伦特,

它不喜欢你的解决方案,所以我试着稍微修改它但它仍然不开心。这是我的调整;

var surveyTopic = (from s in db.Surveys.Where(x => x.Category.Name.Equals(page))

let total = s.Topic.Count()
join r in db.Ratings.Where(y => y.UserName == User.Identity.Name) on s.Topic equals r.Topic
let rated = r.Topic.Count()

where r.Topic == null
select new RatingViewModel 
{
    Topic = s.Topic,
    Category = s.Category.Name,
    Rated = rated+1,
    Total = total
}).FirstOrDefault();