0

In my Asp.net MVC program, I have an action method in a controller like this. There are two pieces of codes which perform the same functionality. The question is, why the comment codes not work?(I checked the view, the comment codes cannot generate anything I desire to see, while the other can.) I guess maybe it has something to do with the for-loop, because variable i disappear from memory after the for-loop end. Any thought is of worth, thanks.

        //
        // GET: /Account/Register
        [AllowAnonymous]
        public ActionResult Register(string code, string state)
        {
           ……

            var departments = new List<IEnumerable<SelectListItem>>();

            //functionally equal, but not work.
//            for (int i=0; i< db.Settings.Find(1).MaxDepartmentLevel;i++)
//            {
//                departments.Add(ToSelectListItems(db.Departments.Where(r => r.Level == i+1)));
//
//            }

            //these work.
            departments.Add(ToSelectListItems(db.Departments.Where(r => r.Level ==  1)));
            departments.Add(ToSelectListItems(db.Departments.Where(r => r.Level ==2)));
            departments.Add(ToSelectListItems(db.Departments.Where(r => r.Level == 3)));


            var registerViewModel = new RegisterViewModel()
            {
                OpenId = wechatUserAccessToken.openid,
                Departments = departments,
                    AvatarImageUrl = avatarImageUrl.TrimEnd('0') + "96"
            };

            return View(registerViewModel);
        }

Edit: db.Settings.Find(1).MaxDepartmentLevel=3.

guo
  • 9,674
  • 9
  • 41
  • 79
  • If you want to keep the variable i, why don't you do something like this: int i=0; for (i=0; i< db.Settings.Find(1).MaxDepartmentLevel;i++) { departments.Add(ToSelectListItems(db.Departments.Where(r => r.Level == i+1))); } – Federico Navarrete Feb 03 '17 at 11:08
  • What value of ```db.Settings.Find(1).MaxDepartmentLevel``` ? – tym32167 Feb 03 '17 at 11:09
  • 2
    it's not the same code, are you sure that db.Settings.Find(1).MaxDepartmentLevel == 3 ,it seems it's 0 – Maksim Simkin Feb 03 '17 at 11:09
  • 1
    Have you tried using First() or ToList(): `departments.Add(ToSelectListItems(db.Departments.Where(r => r.Level == i+1).ToList())` so that the queryable become an department object? Besides, have you debugged the code to see how it is behaving? If the condition is being attended to enter the loop? – dime2lo Feb 03 '17 at 11:12
  • 4
    `for` loop variable closure issue. use temp variable, e.g. `var level = i + 1; departments.Add(ToSelectListItems(db.Departments.Where(r => r.Level == level)));` // – Ivan Stoev Feb 03 '17 at 11:21
  • 1
    It has to work .. did u check what is departments in both cases ? – Kaushik Thanki Feb 03 '17 at 11:56

1 Answers1

2

It's a for loop variable closure issue, which is pointed out by @Ivan in question's comment.

In fact, the code below will not perform as I wish.

  //functionally equal, but not work.
//            for (int i=0; i< db.Settings.Find(1).MaxDepartmentLevel;i++)
//            {
//                departments.Add(ToSelectListItems(db.Departments.Where(r => r.Level == i+1)));
//
//            }

It will perform as this:

            departments.Add(ToSelectListItems(db.Departments.Where(r => r.Level ==  4)));
            departments.Add(ToSelectListItems(db.Departments.Where(r => r.Level ==4)));
            departments.Add(ToSelectListItems(db.Departments.Where(r => r.Level == 4)));

Above, r.Level == 4 because i in for-loop will be 3 finally and i+1=4at last.

r => r.Level == i+1 is instantiating a delegate, which capture variable i itself but not variable i's value. This is totally different from JAVA.

Some valuable references:

http://csharpindepth.com/Articles/Chapter5/Closures.aspx

https://blogs.msdn.microsoft.com/ericlippert/2009/11/12/closing-over-the-loop-variable-considered-harmful/

https://stackoverflow.com/a/227833/5835947

Community
  • 1
  • 1
guo
  • 9,674
  • 9
  • 41
  • 79