-2

I am making a school project which consists of creating a database and reading and writing into it. Within a DataModule I made the database in run time using a TAdoCommand which worked great and now I need to read and write into it. I placed some test data into the database using access but it is unable to read the database.

DataModule Here is a picture of the datamodule in design. I have created a connection, query, datasource and table which are all linked together. The TAdoCommand was used to make the database. The SQL command in the query is "SELECT Username,Password FROM Users"

I then have a Login form in which I hope to use it to read the Users table with the database to check if the user exists in the database.

procedure TLoginFrm.LoginBtnClick(Sender: TObject);
  var Username, Password : String;
      i, NoOfRecords : Integer;
      IsMatch : Boolean;
  begin
    NoOfRecords := modFile.adoDataSet.RecordCount;
    if NoOfRecords = 0 then
    begin
      NewUserFrm.Show;
      Application.Messagebox('There are currently no users. Please create new user.','Error');
      UsernameBox.Text := '';
      PasswordBox.Text := '';
    end
    else
    begin
      IsMatch := False;
      modFile.adoDataSet.First;
      Username := modFile.adoDataSet.FieldByName('Username').AsString;
      Password := modFile.adoDataSet.FieldByName('Password').AsString;
      for i := 1 to NoOfRecords do
      begin
        if (Username = UsernameBox.Text) and (Password = PasswordBox.Text) then
        begin
          LoginFrm.Hide;
          CurrentUser := Username;
          MainMenuFrm.Show;
          IsMatch := True;
        end
        else
        begin
          modFile.adoDataSet.Next;
          Username := modFile.adoDataSet.FieldByName('Username').AsString;
          Password := modFile.adoDataSet.FieldByName('Password').AsString;
        end;
      end;//End of for loop
      if not IsMatch then
      begin
        Application.MessageBox('Incorrect username or password. Try again.','Error');
        UsernameBox.Text := '';
        PasswordBox.Text := '';
        LoginBtn.SetFocus;
      end;
    end;//End of parent Else
  end;

When I put in test data using Access, it returns the message box "Incorrect username or password. Try again". So it recognises that there are more than 0 reccords in the table however it cannot read the actual data. Where did I go wrong?

  • 2
    Why select *all* users when you only need *one*? Why not add a where clause to narrow it down to the user you want? Suppose this database ends up with millions of users. Do you want to fetch them all just to look up one single user? – Jerry Dodge Jan 10 '18 at 15:33
  • Yeh fair enough but I only have a max of 2 users for this school project so I didn't see it being too much of an issue –  Jan 10 '18 at 17:46
  • 2
    If you are disinclined to follow @JerryDodge's advice, you need to learn the art of defensive programming, which involves anticipating and taking account of issues exactly like the one Jerrry has raised. Otherwise your code will become a collection of accidents waiting to happen. – MartynA Jan 10 '18 at 18:48

1 Answers1

1

You're For loop isn't working for you here. Instead you need to iterate your dataset this way:

 else
 begin
   isMatch := false; 
   modFile.AdoDataset.first;
   while not modFile.AdoDataset.eof do
   begin
     Username := modFile.AdoDataset.fieldbyname('Username').asstring;
     Password := modFile.AdoDataset.fieldbyname('Password').asstring;
     if (uppercase(Username) = uppercase(UsernameBox.text)) and (uppercase(Password) = uppercase(PasswordBox.text)) then
     begin
       IsMatch := True;
       LoginFrm.Hide;
       CurrentUser := Username;
       MainForm.Show;
       Exit; // no need to continue on once you have a match
     end;
     modFile.AdoDataset.next;   
   end;
end
else ...

You could also skip using a loop altogether and just use a locate

 else
 begin
   isMatch := modFile.AdoDataset.Locate('Username;Password', VarArrayOf[UsernameBox.text, PasswordBox.text], [loCaseInsensitive]);// remove loCaseInsensitive if you prefer case sensitivity
   if isMatch then
   begin
     CurrentUser := UsernameBox.text;
     Loginfrm.Hide;
     MainForm.Show;
   end;
 end;
John Easley
  • 1,551
  • 1
  • 13
  • 23
  • Thank you very much for your comment! I tried it and it gives the same result of "Incorrect username or password. Try again" :(( –  Jan 10 '18 at 14:20
  • Ahh, realized I didn't include setting your username and password variables. Also, you're test is case sensitive.. – John Easley Jan 10 '18 at 14:34
  • I believe I have entered the Username and Password correctly? [YouTube Demo](https://youtu.be/K_fmhdQZgQY) Here's a demonstration with the amended code –  Jan 10 '18 at 14:40
  • I don't know why but it continues to display the same messagebox –  Jan 10 '18 at 14:43
  • Made more edits.. my guess is you aren't considering case sensitivity – John Easley Jan 10 '18 at 14:52
  • The data in the database are all lower case and I input data into the editbox with lower case. I changed the code to introduce the Uppercase() function but with the same results. The Locate usage looks good however for my project, I am required to use a linear search for marks :/ Thank you very much for your suggestions –  Jan 10 '18 at 14:58
  • 2
    Well, set a break point and use your debugger to evaluate variables to try and determine where the problem might be.. – John Easley Jan 10 '18 at 15:00
  • Okay sure. I'll do that! :) –  Jan 10 '18 at 15:07
  • not sure if `Exit;` is a command you want to learn anybody, otherwise nice answer – GuidoG Jan 10 '18 at 16:56
  • 1
    @GuidoG I wouldn't normally recommend `exit` either, but in this case, looping through potentially an entire dataset, I think it's okay here :) – John Easley Jan 10 '18 at 18:43
  • @MingLau: I get the point that you are not allowed to use `Locate`, but if you are expected to use a linear search, your instructor should at least allow you to traverse the dataset on an index (by user name). Then, you can terminate the loop as soon as the current username is greater that the one you are looking for, as it is then pointless to examine the subsequent ones. Also: 1. Don't use FieldByName in a loop which potentially examines many records - instead use a local TField variable, initialise that **before** the loop to the field in question and inside the loop, compare [cont] – MartynA Jan 10 '18 at 19:33
  • the target user name with the var's AsString property. 2. Don't worry about using `Exit` - it's like the voodoo about never splitting an infinitive - often it is far clearer and more concise to terminate processing than to construct a potentially buggy boolean expression to *continue* processing. – MartynA Jan 10 '18 at 19:34
  • @MartynA Oh right okay thanks. Just wondering why should I use TField variables instead? –  Jan 10 '18 at 20:58
  • 1
    Because `FieldByName` iterates the `Fields` collection of the dataset using a linear search each time it is accessed, which is a relatively slow operation for a single access and in a loop you would be doing it once for each row in the table until you find the row you are looking for. So, obviously it is better to look it up just once, before the loop begins. – MartynA Jan 10 '18 at 21:03