1

I tried with the code below it doesn't work, is there something wrong with my code, please guide me. or if there is another solution

Thanks

Private Sub Btnsave_Click(sender As Object, e As EventArgs) Handles Btnsave.Click
    If txtWCSKILL.Text = "" Or txtWCEXPERIENCE.Text = "" Or txtWCAPPEARANCE.Text = "" Or txtWCEDUCATION.Text = "" Then
        MessageBox.Show("WSKILL,WEXPERIENCE,WAPPEARANCE,WEDUCATION cannot be empty.", "Report Status",
            MessageBoxButtons.OK, MessageBoxIcon.Warning)
        txtWCSKILL.Focus()
        Exit Sub
    End If
    Dim count As Integer

    Using cmd As OleDbCommand = con.CreateCommand()
        cmd.CommandText = "Select COUNT(*) FROM tblWeightingCriteria"
        con.Open()
        If count = CInt(cmd.ExecuteScalar() > 1) Then
            MessageBox.Show("One Record Already Exist!", "Report Status",
                MessageBoxButtons.OK, MessageBoxIcon.Warning)
            con.Close()
            Exit Sub

        Else
            Try
                con.Open()
                cmd.CommandText = "INSERT INTO tblWeightingCriteria" & "([WCSKILL],[WCEXPERIENCE],[WCAPPEARANCE],[WCEDUCATION]) " & "VALUES(@WCSKILL,@WCEXPERIENCE,@WCAPPEARANCE,@WCEDUCATION)"
 cmd.Parameters.AddRange(New OleDbParameter() {
                         New OleDbParameter("@WCSKILL", DbNullOrStringValue(txtWCSKILL.Text)),
                         New OleDbParameter("@WCEXPERIENCE", DbNullOrStringValue(txtWCEXPERIENCE.Text)),
                         New OleDbParameter("@WCAPPEARANCE", DbNullOrStringValue(txtWCAPPEARANCE.Text)),
                         New OleDbParameter("@WCEDUCATION", DbNullOrStringValue(txtWCEDUCATION.Text))})
                Dim result = cmd.ExecuteNonQuery()
                If result = 0 Then
                    MsgBox("No Data has been Inserted!")
                Else
                    MsgBox("New Data is Inserted succesfully!")
                End If
            Catch ex As Exception
                 MsgBox(ex.Message, MsgBoxStyle.Information)
            End Try
        End If
    End Using

    con.Close()
End Sub
JayV
  • 3,238
  • 2
  • 9
  • 14
roy
  • 693
  • 2
  • 11
  • `CInt(cmd.ExecuteScalar() >= 1)`. – dr.null Jul 03 '23 at 09:29
  • @dr.null , Thank you for your reply. 'One Record Already Exist!"' with code 'CInt(cmd.ExecuteScalar() >= 1)' Such a message appears even though the record in the database is empty – roy Jul 03 '23 at 09:46
  • `> 1` means 2 or more. If you need to allow inserting 1 record only then: `Dim count = Convert.ToInt32(cmd.ExecuteScalar()) : If count >= 1 Then ... End If`. Or `If count = 0 Then` Do the insert. Side note: use the [Convert](https://learn.microsoft.com/en-us/dotnet/api/system.convert?view=net-7.0) static methods for conversions. – dr.null Jul 03 '23 at 09:49
  • @dr.null , supposedly if the database is empty or not more than one then do the insert into process whether there is something wrong with my code – roy Jul 03 '23 at 09:49
  • @dr.null , 'Dim count = Convert.ToInt32(cmd.ExecuteScalar()) con.Open() If count >= 1 Then' I have an error I have an error The 'connection's current state is closed.' – roy Jul 03 '23 at 10:00
  • Check your `CommandText`. Why do you concatenate it? Remove the `" & "` and have a space after the table's name. Also, see [this](https://stackoverflow.com/a/40242836/14171304) regarding the named parameters. – dr.null Jul 03 '23 at 10:01
  • `con.Open()` before executing any command. – dr.null Jul 03 '23 at 10:06
  • @dr.null , Sorry I replied to you too late – roy Jul 03 '23 at 11:36
  • @dr.null , 'the connection was not closed. The Connection's current state is open' still problem. – roy Jul 03 '23 at 11:45
  • @dr.null , `Using cmd As OleDbCommand = con.CreateCommand() con.Open() cmd.CommandText = "Select COUNT(*) FROM tblWeightingCriteria" Dim count = Convert.ToInt32(cmd.ExecuteScalar()) If count >= 1 Then MessageBox.Show("One Record Already Exist!", "Report Status", MessageBoxButtons.OK, MessageBoxIcon.Warning) con.Close() Exit Sub else` – roy Jul 03 '23 at 11:49
  • Yes, good piece. What about it? _Still problem_ which is? – dr.null Jul 03 '23 at 14:33
  • @dr.null , yes still problem – roy Jul 03 '23 at 14:35
  • @dr.null , `the connection was not closed. The Connection's current state is open` still problem – roy Jul 03 '23 at 14:39
  • Read Albert's answer about the connection. Don't create a backing field for the connection, just create one when you need one. – dr.null Jul 03 '23 at 14:42
  • @dr.null , after `try` so I should comment out `con.Open()` – roy Jul 03 '23 at 14:50
  • @dr.null , So the solution and guide that works perfectly – roy Jul 03 '23 at 14:53
  • @dr.null , So guide from you then you can give as an answer then it will mark your answer – roy Jul 03 '23 at 14:54
  • @dr.null , sorry I replied late, the guide from you who made no more problems so I told you to make an answer so I can immediately mark and thank you – roy Jul 04 '23 at 02:06

2 Answers2

1

First of all, you don't show where your connection object was created.

Coming from MS Access, it is long time coding approach to persist the connection, but you do not do this when using .net. In other words, don't try and persist a connection object. So, when writing code in MS Access + VBA, you can and often should force a connection to remain open to the database.

In vb.net, you don't have to do this, and in fact you don't want to.

Next up, fire up access and create a query, and type in this:

enter image description here

Note that EVEN when there are zero rows in the database, you STILL get a row back!

When I run the above query, I get this result:

enter image description here

So, the data returned is ONE row of data, and a count = 0.

So, you will ALWAYS get 1 record from such a query, EVEN when zero rows exist in the table.

Your logic is that once a record is inserted to that table, you will NEVER for the next 100 years ever again insert a row into that table. That means the insert code can and will only run one time for the WHOLE LIFE of the application.

I suspect that you looking to test/check if the current record exists, since the logic you have so far does not make a whole lot of sense.

I mean, once you inserted a record into that table, you are to never use that that one row, and you NEVER insert again into that table?

So, in theory, you have a table that will only ever have one row of data, but after you insert a record, you will never insert data again to that table?

Are you sure that's the design you want here? So that table only is ever to have one inserted row of data, and from that point for next 100 years, you never add another row?

I guess the big question then do you ever want to update or change that one row in that table that will only ever have one row?

And does your above code ever supposed to update that one row again?

Now, I can't tell if you logic is:

  • We only ever for 100 years insert one row, and NEVER touch that one row of data again for the next 100 years?

  • Or we want to check if no rows exist, and if no rows, we insert the one row, however, if one row exists, then I want to update that one existing row with the new values?

If no rows, insert new row with your values.

If 1 existing row, then update that row with your new values.

Seems to me, the above makes more sense.

However, let's write this to only EVER for the next 100 years insert the one row , and thus NEVER again ever insert.

So, this code looks to be workable:

    If txtWCSKILL.Text = "" Or
        txtWCEXPERIENCE.Text = "" Or
        txtWCAPPEARANCE.Text = "" Or
        txtWCEDUCATION.Text = "" Then
        MessageBox.Show("WSKILL,WEXPERIENCE,WAPPEARANCE,WEDUCATION cannot be empty.", "Report Status",
            MessageBoxButtons.OK, MessageBoxIcon.Warning)
        txtWCSKILL.Focus()
        Exit Sub
    End If

    Using cmd As New OleDbCommand("SELECT * FROM tblWeightingCriteria")
        Using conn As New OleDbConnection(My.Settings.TEST4)
            conn.Open()

            Dim rstData As New DataTable
            rstData.Load(cmd.ExecuteReader)

            If rstData.Rows.Count > 0 Then
                MessageBox.Show("One Record Already Exist!", "Report Status",
                MessageBoxButtons.OK, MessageBoxIcon.Warning)
                Exit Sub
            End If

            ' does not exist, so create the row
            Dim MyRow As DataRow = rstData.NewRow
            MyRow("WCSKILL") = txtWCSKILL.Text
            MyRow("WCEXPERIENCE") = txtWCSKILL.Text
            MyRow("WCAPPEARANCE") = txtWCAPPEARANCE.Text
            MyRow("WCEDUCATION") = txtWCEDUCATION.Text
            rstData.Rows.Add(MyRow)
            Dim da As New OleDbDataAdapter(cmd)
            Dim daU As New OleDbCommandBuilder(da)
            da.Update(rstData)
        End Using
    End Using

Note that we ALWAYS assume the connection was and will be and has been closed. If you get a error about the connection open already, then we can assume you have a bug in your code.

Now, it is VERY possible that you need/want to update the 1 row with the new values, but create the row if ZERO rows exist. Thus, we can use the above same code for a insert, or a update.

So, the code then becomes this:

(logic: if no rows, create + then edit) (if existing row, then edit)_

    If txtWCSKILL.Text = "" Or
        txtWCEXPERIENCE.Text = "" Or
        txtWCAPPEARANCE.Text = "" Or
        txtWCEDUCATION.Text = "" Then
        MessageBox.Show("WSKILL,WEXPERIENCE,WAPPEARANCE,WEDUCATION cannot be empty.", "Report Status",
            MessageBoxButtons.OK, MessageBoxIcon.Warning)
        txtWCSKILL.Focus()
        Exit Sub
    End If

    Using cmd As New OleDbCommand("SELECT * FROM tblWeightingCriteria")
        Using conn As New OleDbConnection(My.Settings.TEST4)
            conn.Open()

            Dim rstData As New DataTable
            rstData.Load(cmd.ExecuteReader)

            Dim MyRow As DataRow
            If rstData.Rows.Count > 0 Then
                ' row does not exist, so create it
                MyRow = rstData.NewRow
            Else
                ' row eixsts, so we will modify this existing row
                MyRow = rstData.Rows(0)
            End If

            MyRow("WCSKILL") = txtWCSKILL.Text
            MyRow("WCEXPERIENCE") = txtWCSKILL.Text
            MyRow("WCAPPEARANCE") = txtWCAPPEARANCE.Text
            MyRow("WCEDUCATION") = txtWCEDUCATION.Text
            If rstData.Rows.Count = 0 Then
                rstData.Rows.Add(MyRow)
            End If

            Dim da As New OleDbDataAdapter(cmd)
            Dim daU As New OleDbCommandBuilder(da)
            da.Update(rstData)

        End Using
    End Using

And note in above code, we CREATE the connection object each time. You have to change my.Settings.TEST4 to your connecting that you build and placed in the application settings (since you not going to type in connecting string in the code, as that too difficult to maintain).

halfer
  • 19,824
  • 17
  • 99
  • 186
Albert D. Kallal
  • 42,205
  • 3
  • 34
  • 51
  • _So, the data returned is ONE row of data, and a count = 0._ How is that? Count should be `1` if there's a single row. No, what you see here is not a Row, it's how the COUNT result is reported and displayed. Also, use the [short-circuiting](https://en.wikipedia.org/wiki/Short-circuit_evaluation) operators. `OrElse`, `AndAlso`. Note, `da` and `daU` are disposable objects. – dr.null Jul 03 '23 at 14:30
  • Right, but EVEN if there are zero rows in the table, the query STILL returns one row of data!!! a count(*) STILL returns a row count of 1. In my example code, I not using count(*), so if there are no rows, then count = 0 in my example code. So using count(*) will always return one row, and it will return one row if there are zero rows in the table, or 10,000 rows. So, count(*) returns the count of rows, not that the result will not have any rows!!! You have to pluck out, use, grab the first row and first column if you want to get the row count, even when 0 (no rows). – Albert D. Kallal Jul 03 '23 at 14:33
  • Well, if you want to 100% change the query and what you are doing? Sure, no kidding! But the context of the point was a select Count(*) from mytable, and that will ALWAYS return one row, and even when no rows exist in the table. So, sure, if you want to change the query, change the point I am making, then no question you get a different result. However, since "id" or other columns was not in the query, then obviously my point still 100% stands. Checking the number of rows returned thus does not make sense for a select count(*) query, since it will return one row in all cases. – Albert D. Kallal Jul 03 '23 at 14:51
  • 1
    As I stated, my best guess is that if there are zero rows, then the row is to be added. However, if the row exists, then we want to update. That is a "guess" on my part, but I am betting that is really what is wanted here. Thus, I posted the 2nd code snip which does updates the 1 row, or adds the row if none exist. I also used a datatable since the JET/ACE data engine does not respect order of parameter's (they have to match same order as adding the paramters). So, often I think using a data table with ms-access is a better choice, since then order of the columns does not matter anymore. – Albert D. Kallal Jul 03 '23 at 15:03
1

Several points to consider.

You have this line:

If count = CInt(cmd.ExecuteScalar() > 1) Then

count is of type Integer and CInt(cmd.ExecuteScalar() > 1) returns a Boolean value, so obviously you have the Option Strict set to Off and the implicit conversions are allowed. If you turn the option On, you'll get the BC30512 error:

Option Strict On disallows implicit conversions from 'Boolean' to 'Integer'.

To avoid getting unexpected results and errors like this, make sure to turn this and other on/off options on by code:

Option Explicit On
Option Strict On
Option Infer On

Or through the project's properties:

Project -> YourProjectName Properties -> Compile.

and turn the Explicit, Strict, and Infer On. I'm sure your debugger will start complaining and report some errors to fix. At least, consider doing this in your next project.


The COUNT(*) or COUNT(TheIdField) query returns the n rows/records of the given table. If the table is empty, then you'll get 0 for sure. So, if you want to allow inserting just one record:

Using con = New OleDbConnection("..."), cmd = con.CreateCommand()
    cmd.CommandText = "select count(*) from tblWeightingCriteria"
    con.Open()

    Dim count = Convert.ToInt32(cmd.ExecuteScalar())

    If count > 0 Then
        ' Alert and exit the sub.
        Exit Sub
    End If

    ' Otherwise, proceed...
End Using

Note, don't keep the data access objects (OleDbConnection, OleDbCommand, OleDbDataAdapter, ...etc) in class fields. Create them in the CRUD methods in Using block. This way, you don't need to explicitly close a connection or dispose of a disposable object. The Using statement will do that for you.


Separate your code and create specialized methods.

To create a connection:

Private Function CreateConnection() As OleDbConnection
    Return New OleDbConnection("Your connection string;")
End Function

For the CRUD operations and helpers methods:

Private Function InsertData() As Integer
    Using con = CreateConnection(), cmd = con.CreateCommand()
        cmd.CommandText = "INSERT INTO tblWeightingCriteria " &
            "([WCSKILL], [WCEXPERIENCE], [WCAPPEARANCE], [WCEDUCATION]) " &
            "VALUES (?, ?, ?, ?)"
        cmd.Parameters.AddRange({
            New OleDbParameter("?", OleDbType.VarChar) With {
            .Value = DbNullOrStringValue(txtWCSKILL.Text)},
            New OleDbParameter("?", OleDbType.VarChar) With {
            .Value = DbNullOrStringValue(txtWCEXPERIENCE.Text)},
            New OleDbParameter("?", OleDbType.VarChar) With {
            .Value = DbNullOrStringValue(txtWCAPPEARANCE.Text)},
            New OleDbParameter("?", OleDbType.VarChar) With {
            .Value = DbNullOrStringValue(txtWCEDUCATION.Text)}
        })
        con.Open()
        Return cmd.ExecuteNonQuery()
    End Using
End Function

Private Function DbNullOrStringValue(value As String) As Object
    If String.IsNullOrEmpty(value) Then
        Return DBNull.Value
    Else
        Return value
    End If
End Function

Private Function LoadData() As DataTable
    Dim dt As New DataTable()

    Using con = CreateConnection(), cmd = con.CreateCommand(),
        ta = New OleDbDataAdapter(cmd)
        cmd.CommandText = "Select * From tblWeightingCriteria"
        ta.Fill(dt)
    End Using

    Return dt
End Function

Private Function GetCount() As Integer
    Using con = CreateConnection(), cmd = con.CreateCommand()
        cmd.CommandText = "select count(*) from tblWeightingCriteria"
        con.Open()
        Return Convert.ToInt32(cmd.ExecuteScalar())
    End Using
End Function

' ...etc.

Note, the OLE DB provider does not support named parameters. Use ? as placeholders and add the parameters in the same order of the database fields as shown above.

Now your save caller should look like this:

Private Sub btnInsert_Click(sender As Object, e As EventArgs) _
    Handles btnInsert.Click
    Dim inputBoxes = {
        txtWCSKILL,
        txtWCEXPERIENCE,
        txtWCAPPEARANCE,
        txtWCEDUCATION
    }

    If inputBoxes.Any(Function(tb) tb.Text.Trim().Length = 0) Then
        MessageBox.Show("... cannot be empty.",
                        "Report Status",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Warning)
        inputBoxes(0).Select()
        Return ' or Exit Sub            
    End If

    Try
        If GetCount() > 0 Then
            MessageBox.Show("One Record Already Exists!",
                        "Report Status",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Warning)
            Return
        End If

        Dim result = InsertData()

        If result = 0 Then
            MessageBox.Show("No Data has been Inserted!")
        Else
            MessageBox.Show("New Data is Inserted succesfully!")
        End If
    Catch ex As Exception
        MessageBox.Show(ex.Message, "Error",
                        MessageBoxButtons.OK, MessageBoxIcon.Error)
    End Try
End Sub

Also, consider implementing the n-tier or a similar purpose architecture. Have the UI or the presentation in a layer/tier in one project. The data access in another layer/tier (DAL) and project. And a middle business logic layer (BLL) to communicate between them.

Further reading and examples.

dr.null
  • 4,032
  • 3
  • 9
  • 12