Resolved Question on Lambda Expressions in LINQ and MVC

Anonymous

Well-known member
Joined
Sep 29, 2020
Messages
84
Programming Experience
Beginner
Hello I have always struggled with Lambda expressions in LINQ. I am trying to understand the following code that I found online . I know this might be a stupid question but I did try to read the documentation but couldnt understand much. I would be highly grateful if you could explain in simple words.

This is the controller code of a simple login page application

C#:
[HttpPost]
        public async Task<IActionResult> Login(Login login)
        {
            if (ModelState.IsValid)
            {
                var User = from m in _context.Login select m;
                User = User.Where(s => s.username.Contains(login.username));
                if (User.Count() != 0)
                {
                    if (User.First().password == login.password)
                    {
                        return RedirectToAction("Main","Main");
                    }
                }
            }
            return RedirectToAction("LoginFailed");
        }


This is the model class:

C#:
    [Table("users")]
    public class Login
    {  
        [Key]
        public int userId { get; set; }

        [Required(ErrorMessage = "Username is requried!")]
        public string username { get; set; }

        [Required(ErrorMessage = "Password is requried!")]
        public string password { get; set; }
    }

I know that

C#:
var User = from m in _context.Login select m;
                User = User.Where(s => s.username.Contains(login.username));

are LINQ queries. The first line is fetching the list of users and the second line is filtering that list of users.

I am not able to understand that how is the first line able to fetch user list from the database. Like how does "m" will fetch the user list?

In the documentation here Part 7, add search to an ASP.NET Core MVC app

it is written that "The query is only defined at this point, it has not been run against the database." So, when does it run against the database?

Secondly, what is the difference between s.username and login.username?

and why are they comparing User.First().password == login.password? Are they comparing one user with the entire list of users?
 
but couldnt understand much

Your first problem is that you're naming things poorly.

C#:
var User = from m in _context.Login select m;

This should be:

C#:
var logins = from m in _context.Logins select m;

Or even:

C#:
_context.Logins


Don't name collections with a singular. Don't name sole instances of objects in the plural. Don't name classes in the plural; if a class represents a collection of objects, suffix its name with Collection. Strive to avoid creating collection classes anyway, use composition and have objects that have collections of other objects rather than being collections of other objects


Look at this:

C#:
var User = new User[10];

This is wrong; aside from using a capital U when it's a local variable, it's an array of User, but it's been called "User" as if it's a single user. This means that the code stops reading like a book:

C#:
if(User.Length) // the length of a user? 190cm? 6 inches? what?

Naming collections in the plural helps you remember that theyre collections, and you can use LINQ or other collection treatments on them:

C#:
foreach(var u in User)  //how many of what in a User now?
 
foreach(var user in users) //ahh, users is a collection, user is a single User in the collection
 
var tallUsers = users.Where(user => user.Height > 190) //"users where 'the user's height is over 190' is true"

"tallUsers" plural, not "tallUser" -> the output of Where is another collection of User, not a single User, so make the plurality agree with the collection/lone item aspect


---

I said "or even _context.Logins" because there really isn't much point doing from x in somecollection select x - it just gets you a colelction of everything in somecollection, which is essentially the same as somecollection
 
Last edited:
Banging the naming conventions drum a bit more, properties use PascalCase, not cameCase or lowercase. Your entire code block should look more like:

C#:
        public async Task<IActionResult> Login(Login login)
        {
            if (ModelState.IsValid)
            {
                //or consider context.Logins.FindAsync(login.Username)
                var u = await _context.Logins.FirstOrDefaultAsync(user => user.Username == login.Username);
                
                if(u != default && u.HashedPassword == PasswordHashImpl(login.Password))
                    return RedirectToAction("Main","Main");
            }
            return RedirectToAction("LoginFailed");
        }

u is singular now, because I ask for the first User (context.Logins should really be called context.Users) where the username equals (not contains; imagine how embarassing if "fred" ends up logged in as frederick just because he happens to have the same password) the username typed in the web form

u is either a user with that exact name, or itis null. If it is null/default there was no user with that name. If it is not null we check the password

We never store passwords in plain text; people reuse passwords. When your system is hacked and those plaintext passwords are stolen, it's a major nuisance for the users. Always hash and salt your passwords and store the hashed salted result. When checking the password the user gave yopu don't decrypt the stored password, you encrypt(hash) what they gave you and then compare the stored encrypted version with the given encrypted version. If the data matches they provided the right password
 
I am not able to understand that how is the first line able to fetch user list from the database. Like how does "m" will fetch the user list?

It doesn't do anything more than using context.Logins would

In the documentation here Part 7, add search to an ASP.NET Core MVC app

it is written that "The query is only defined at this point, it has not been run against the database." So, when does it run against the database?

LINQ is a set of methods that either return enumerables or return single results. If you use a single result, like All or First, then the database is queried at that moment. If you use a method that returns an enumerable (like Where) then the database is queried at the point in time that you, or something you do, causes enumeration of the enumerable. That might be using First, or ToList, or running a foreach over it. In simple terms, the query is run when you try to read its results, not when you call it. In your case it is First() that is triggering the query to run because First runs a foreach that quits after one go round the loop, but doing the loop ran the query

C#:
var tallUsers = users.Where(u => u.Height > 190)

Right now the `tallUsers` variable does not contain a list of users over 190, it contains an enumerable thing that will look for users over 190 when you foreach it, or ToList it, or First it etc. That could be hours later. It usually doesn't really matter, but you need to maintain awareness that data can change between setting the query up and running it. You could do this:

C#:
var tallUsers = users.Where(u => u.Height > 190);

var tallUsers1 = tallUsers.ToArray();

users.Add(new User{ Height = 200 });

var tallUsers2 = tallUsers.ToArray();

tallUsers1 is an array that has N Users in it. tallUsers2 has N+1 users in it - there was a delay between running the query the first time and running it the second. More users met the criteria the second time. Setting the query up didn't cause anything to be run or evaluated or deliver any values.. It's only when ToArray reads the query results that the query is run

If you wanted to run it instantly:

C#:
var tallUsers3 = users.Where(u => u.Height > 190).ToArray()

tallUsers3 is now an array of users that are over 190; ToArray has foreach'd the Where result for you, and copied the results into an array. That's fixed; if you add more entities to users they won't automatically come into tallUsers3.

If you want an analogy, think about files on disk. If you have a word document that says "hello" and you save it then make a shortcut to it, the shortcut is like the LINQ query. It is resolved to a file every time you double click it. If you open the file, change it to say "goodbye" then double click the shortut again, you see the new content. The file is loading and showing as it is when the shortcut is double clicked. If instead you took a copy of the file (tallUsers1 is like a copy) you can edit the original file, double click the copy and not see the change in the copy. LINQ queries are a set of operations that are performed on the original data source when you read them, not when you set them up. The shortcut will always read the latest content of the original file at the moment it is double clicked, not the moment when it is set up/created

Secondly, what is the difference between s.username and login.username?

s.username is a reference to the username of any one of the users in the Logins table. login.username is the username the user typed into the web page and was passed into the method in the Login object

Let's talk a bit about lambdas. They're essentially nameless methods with a particular syntax. This code:


C#:
users.Where(u => u.Username == "fred")

is notionally the same as:

C#:
users.Where(UserNameIsFred)

private bool UserNameIsFred(User u){
  return u.Username == "fred";
}

The second form should be familiar to you; there is a method that returns a bool after taking in a user and comparing its username to "fred"

If you think about ways to cut that down so we type fewer chars:

* the compiler should know the type of the input argument just by looking at what we passed when we called it
* the compiler should know the return type by looking at what we return
* we dont need a name, because the compiler can invent one and use it internally
* we can get rid of access modifiers like private, because the compiler can choose them for us
* we can do away with return keyword, and even the multi line block brackets, if we have a rule that the line of code that develops the value to be returned shall be the only line of code there is in the method


This means that this can become:

C#:
private bool UserNameIsFred(User u){
  return u.Username == "fred";
}

//get rid of argument type
private bool UserNameIsFred(u){
  return u.Username == "fred";
}

//get rid of return type
private UserNameIsFred(u){
  return u.Username == "fred";
}

//get rid of name
private (u){
  return u.Username == "fred";
}

//get rid of access modifier
(u){
  return u.Username == "fred";
}

//get rid of blocks and return
(u) => u.Username == "fred"

//heck, we have only one argument, lets get rid of the round brackets too
u => u.Username == "fred"

We've ended up with our lambda from our method:

C#:
//get rid of access modifier
users.Where(u => u.Username == "fred")

Where is simply a method that accepts another method(a lambda) as its argument. It is called on a collection, and it will visit every object in the collection, invoking the passed-in method(lambda) on the object from the collection. If the lambda returns true, then the object is ouput. If the lambda returns false, the object is not output. ps; we know u is a User because we're calling it on a colelction of User - users is perhaps a List<User> etc

Why do this? Why have this thing where we pass methods around like we pass data around? Well.. Microsoft cannot know what you will want to do to your collections, or even what will be in them. The most flexible thing to do is just give you the Where method, and let *you* write another method that determines whether you want the object (you want the user whose name is fred) in the returned collection or not. All MS have to do is take the method you wrote, and call it on everything in the collection one by one, and then give you the results.

If it still doesn't make sense, think about event handlers in a GUI; microsoft can write a Button class that draws a button, but they cannot know what you want to do when it is clicked, so they simply make a facility where you can tell the button "here is a method with some particular signature you require; call it when the user clicks you" - that way you write the missing functionality that it pertinent to your app, and MS's Button just calls it

We don't need LINQ, and Where - you could do similar with:

C#:
var newUsers = new List<User>();
foreach(var u in users)
  if(u.Username == "fred")
    newUsers.Add(u);
return newUsers;

but its a lot more hassle than:

C#:
var fredUsers = users.Where(u => u.Username == "fred").ToList()

However, that pretty much is all Where does:

C#:
var newUsers = new List<User>();
foreach(var u in users)
  if(METHOD_YOU_PROVIDED(u))
    newUsers.Add(u);
return newUsers;

It calls the method you provided and if it returns true, the particular user makes it into the output

I've skipped over an important part about how Where works and yields results, because it's somewhat accessory to this discussion, but you'll come acorss it eventually. It's not important for now.

and why are they comparing User.First().password == login.password? Are they comparing one user with the entire list of users?
First() gets the first thing out of a list of things; that's a lone entity, with a single password. login is a lone entity. This is just a string == string by the time users.First() has been evaluated

I must stress the point though; name collections (arrays, lists, sets, dictionaries) and things that behave like them (enumerable things) in the plural; it massively reduces confusion
 
Last edited:
You're welcome (but please avoid quoting huge messages and then just writing a single sentence underneath the quote - it needlessly takes up a lot of space on the page. It is better to use the Quote facility only to pick out small parts of another post e.g. if the post asks several questions and you want to reply to them case by case)
 
Back
Top Bottom