Read from NTX files with Vb.NET - vb.net

I'm trying to read from some DBF files that use NTX files for indexing within VB.NET. Currently, I'm having to read directly from the DBF files using OLEDB, which is ridiculously slow due to dbase's flat file method of data storage. So, I'm wondering if someone could tell me how to access the DBF files through their NTX index files within VB.NET.
If I need to download a third party library I'm okay with that, But I don't have money to pay for a third party library if it costs money. It would need to be free.
This is what I'm currently using to access the DBF Files.
Private Shared ConnectionString As String = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & My.Settings.DataPath & ";Extended Properties=dBase IV"
Public Shared dBaseConnection As New System.Data.OleDb.OleDbConnection(ConnectionString)
Dim dBaseCommand As New System.Data.OleDb.OleDbCommand("SELECT * FROM `PAGES.NTX` WHERE `PAGE_NUM` BETWEEN 241 AND 270", dBaseConnection)
Dim dBaseDataReader As System.Data.OleDb.OleDbDataReader = dBaseCommand.ExecuteReader(CommandBehavior.SequentialAccess)
This however still reads directly from the DBF file and ignores the NTX indexing. Any help?
Note: I cannot "choose" to use SQL for this project as the DataBases are ones created and maintained by another application (One of considerable age). I need only access them for the data stored within.

Download Advantage .NET Data Provider
This is the only example I could find. Since I do not have the files to test with this is only an example:
'Set the TypeTable to NTX
Dim conn As New AdsConnection("data source=c:\data;" + "ServerType=remote|local; TableType=NTX")
Dim cmd As AdsCommand
Dim reader As AdsDataReader
Dim iField As Integer
Try
' make the connection to the server
conn.Open()
' create a command object
cmd = conn.CreateCommand()
' specify a simple SELECT statement
cmd.CommandText = "select * from departments"
' execute the statement and create a reader
reader = cmd.ExecuteReader()
' dump the results of the query to the console
While reader.Read()
For iField = 0 To reader.FieldCount - 1
Console.Write(reader.GetValue(iField) + " ")
Next
Console.WriteLine()
End While
conn.Close()
Catch e As AdsException
' print the exception message
Console.WriteLine(e.Message)
End Try

Related

Additional information: There is already an open DataReader associated with this Command which must be closed first. vb.net

Dim cat As New Catalog()
Dim con As New OleDbConnection()
Dim cmd As New OleDbCommand
Dim ds1 As New DataSet
Dim conn As ADODB.Connection
' Open the Access database.
conn = New Connection
conn.ConnectionString =
"Provider=Microsoft.Jet.OLEDB.4.0;" &
"Data Source=" + openExcel + "\Test" + ".mdb; Persist Security Info=False"
con.ConnectionString =
"Provider=Microsoft.Jet.OLEDB.4.0;" &
"Data Source=" + openExcel + "\Test" + ".mdb; Persist Security Info=False"
conn.Open()
xlWorkSheet1.Columns(5).Insert
Dim cellValue As String = ""
Dim newValue As String = ""
Dim sh1 As String = ""
Dim qty As String = ""
Dim matchText As String = ""
Dim sql As String = ""
con.Open()
sh1 = LTrim$(xlWorkSheet1.Cells(i, 1).Text)
sql = "SELECT Num_ber, Q_ty FROM good WHERE Na_me LIKE 'staff%' And Ty_pe = 'ORD'"
Dim cmd As New OleDbCommand(sql, con)
Dim myReader As OleDbDataReader = cmd.ExecuteReader()
conn.Execute(sql)
myReader = cmd.ExecuteReader() ' HERE'S THE PROBLEM
xlWorkSheet1.Cells(1, 5) = myReader.GetString(0)
xlWorkSheet1.Cells(1, 11) = myReader.GetString(1)
myReader.Close()
conn.Close()
conn = Nothing
**I wanted to retrieve a specific value from mdb and then write it to excel.
Here's my code, I got this error so many times and I can't find it out. Can anybody help me? Thanks.**
[1]: https://i.stack.imgur.com/6Fuvg.png
Ok, you first have to decide when usng .net what "provider" you are going to use, AND THEN decide what kind of data objects you want to use.
You can use a oracle provider, a sql server provider, or in this case, since we using Access, then we can use EITHER oleDB, or ODBC. Either choice is fine. Most use oleDB providers for Access, but often ODBC is a good choice, especially if down the road you plane to swap out Access for say SQL server.
What the above means in plain English?
You don't want to adopt the external ADODB code and library. That code is NOT .net, and thus you REALLY but REALLY do not want to write your .net code that way. ADODB was written LONG before .net, and is what we call un-managed code (non .net). I strong, but strong suggest you do NOT add a reference to ADODB to your project, and I beyond strong recommend you avoid introduction of a non .net library for doing this!!! We certainly can adopt the oleDB provider in .net, but we will NOT have a direct reference to JET/ACE (the access database engine) in our applcation. As noted, there are some exceptions to this suggesting, but they don't apply to you and here.
Next up:
The design pattern in .net is to create the connection, get the data, and CLOSE the connection. This "pattern" will then be 100% sure that the data base is always closed, and you NEVER have to worry about if the connection is open, closed, or even if you forgot to close the connection!!! So, do this correct, and some "open" connection will never bite you, or will you have to worry about this issue.
You can in some operations keep the connection open for performance, but lets learn to walk before we run so to speak.
next up:
We certainly do NOT want to place and have connection strings all over in our code. Not only is this going to wear out your keyboard, but if you ever need to change the connection, then you going to have to hunt down all that code.
Best to let Visual Studio save that connection in ONE location, and MORE important MANAGE this for you!!!
Next up:
Do you ONLY need to work with mdb files, or do you plan/need to work with accDB files? This is a HUGE issue, and one that you cannot ignore.
next up:
Are you going to use the x32 bit version of the Access database system, or the x64 bit version?
Since your example posted code uses JET (access data engine for mdb files ONLY x32 bit version)?
Then this ALSO means you MUST (and I repeat MUST) force your .net project to run as x32 bits. You cannot use "any cpu", and you cannot use x64 bits, you MUST choose x86 bit size for your .net project. Failure to do so will result in the project not working.
Ok, with the above information?
First up, force/set/be 100% sure your project is set to run as x32 bits.
That setting is this one:
and remove the reference you have to ADO if you created one.
Ok,
next up:
Create the connection to the database.
Project ->properties.
This here:
And then:
and then
Now, you can browse, and select the access mdb file.
But, you MUST not skip the next step - you have to choose JET (older, mdb files), or choose the newer ACE (for accDB format files).
So, this:
now this:
As noted, you choose JET or ACE here.
now, we have this and you can use test connection.
BUT BE VERY careful!!!!
If you are using vs2022, then keep in mind vs2022 is the FIRST version of VS that is now x64 bits. As a result, vs can't pass the test connection!!! Your connection is in fact ok, but will fail with vs2022.
If you using a previous version of VS (before 2022), then the test connection button should work. and you see this:
Ok, now that we have a valid working conneciton setup, we can now write code, and we will NOT use ADODB!!!!
The typical code pattern to read and load a data table (like a access VBA recordset) will be like this:
Now, I became RATHER tired of writing that same using block over and over. So, in a global module, I have this code now:
Public Function MyRst(strSQL As String) As DataTable
Dim rstData As New DataTable
Using conn As New OleDbConnection(My.Settings.AccessDB)
Using cmdSQL As New OleDbCommand(strSQL, conn)
conn.Open()
rstData.Load(cmdSQL.ExecuteReader)
End Using
End Using
Return rstData
End Function
So, now with the above handy dandy helper routine?
Your code becomes this:
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim Sql As String =
"SELECT Num_ber, Q_ty FROM good WHERE Na_me LIKE 'staff%' And Ty_pe = 'ORD'"
Dim rstData As DataTable = MyRst(Sql)
Debug.Print("Na_me is " & rstData.Rows(0).Item("Na_me"))
End Sub
Or, display all return rows from that data table
Debug.Print("Na_me is " & rstData.Rows(0).Item("Na_me"))
For Each OneRow As DataRow In rstData.Rows
Debug.Print("na_me = " & OneRow("Na_me"))
Next
So, you really don't need (or want) a reader anyway. Just load the results into a nice clean, easy to use data table, and from that you can loop the table, grab rows, or do whatever you want.

Can't access Local SQL database manually by connection string in VB.NET using Visual Studio

I have a project set up with 3 separate tables. I used the built-in designer from Visual Studio.
The tables contain
Students
Courses
Tests in each course
All the tables share a studentnumber.
I have no problem getting and accessing the data true different tableadapters and corresponding bindingsources automatically generated by Visual Studio when I "drag and drop" them on the forms. I also am able to use the query builder to get the data I need from each table.
My problem occurs when I try to access the database manually via connection string.
I get this error:
Cannot attach file local path\HovedDatabase.mdf as database Outcomes because the file is already in use for database local path\HovedDatabase.mdf
This is the code I have:
Private Sub FormPrøver3_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'TODO: This line of code loads data into the 'StudentDataSet.StudentTabell' table. You can move, or remove it, as needed.
Me.StudentTabellTableAdapter.Fill(Me.StudentDataSet.StudentTabell)
Me.StudentTabellTableAdapter.Connection.Close()
Get_Data_Manualy_Fag()
End Sub
Private Sub Get_Data_Manualy_Fag()
Try
'Create variabals for inputcontainer
Dim Studnr As String = Me.StudentnrLabel1.Text
'Create a connection to the DataBase
Dim myConn As SqlConnection
myConn = New SqlConnection("Initial Catalog=OutComes;Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\HovedDatabase.mdf;Integrated Security=True")
'Create a Command object.
Dim myCmd As SqlCommand
myCmd = myConn.CreateCommand
myCmd.CommandText = "SELECT Fag FROM Fag3Tabell WHERE Fag = #Studnr"
myCmd.Parameters.AddWithValue("#Studnr", Studnr)
'Open the connection.
myConn.Open()
'Process the answer
Dim myreader As SqlDataReader
myreader = myCmd.ExecuteReader
'Traverse the DataSet and Display :
Dim i As Integer = 0
Do While myreader.Read()
Me.ComboBox1.Items.Add(myreader.GetString(i))
i = i + 1
Loop
'Close the connection
myConn.Close()
'Handle errors
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
I set up a similar project with a similar LocalSQL database. I have done nothing in Visual Studio other than creating the database with the same tables.
Here I have no problem connecting to the database via manual connection string.
My question is: why can't I access the local DB with a connection string in the first project?
You haven't posted your other connection string, but it looks like AttachDbFilename may be the problem. The DB will already be attached, this option is only meant to be used in single-user mode (one connection only).
Best bet is to permanently attach the DB to the LocalDB instance if that is what you are using the whole time.

How can I allow multiple users to access/edit a microsoft access database?

Now I know right off the bat that Microsoft access isn't the ideal client for multiple users accessing it but it's the only one I've got right now. I have built a small program as a sort of inventory management system. There are currently three users that will be using it regularly and at the same time. One issue I am running into with this is that sometimes the database will not be accessible and will give an error stating that the file is already in use by "so and so" user. The other issue is that I'm getting a similar error every now and then where it states "The database has been placed in a state by user on machine that prevents it from being opened or locked". I am connecting to the database through an ACE OLEDB connection using the line below
con.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=P:\Tool & Cutter Grinding\Tool Cutter Database.accdb;Persist Security Info = False"
I have also changed some of the settings in the actual access database such as:
Enable all macros
Add the folder the database is in to the trusted locations list
Confirm that the database is set to open in shared mode by default
I don't know if there is something small I've missed or a setting I need to change but as of yet, the problem is still persisting.
Below is an example of how I am using the database. I am using string based SQL commands but am not too familiar with DataSet/DataTable/etc. items, so I may be doing something incorrectly.
'close connection from any previous session
con.Close()
'clear dataset so as not to append data
ds.Clear()
'Select SQL query that selects ALL records from a table
Dim str As String = "SELECT * FROM " & "[" & table & "]" & ""
con.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=P:\Tool & Cutter Grinding\Tool Cutter Database.accdb;Persist Security Info = False"
'use try catch statement to open the connection
Try
con.Open()
Catch ex As Exception
MsgBox(Convert.ToString(ex))
End Try
'use try catch statement to add a table (dt) to the dataset (ds) in order to store values
Try
ds.Tables.Add(dt)
Catch ex As Exception
End Try
'create new dataadapter object using the sql string from above and the connection created above
da = New OleDbDataAdapter(str, con)
'create new command builder in order to excecute the SELECT SQL statement using the dataadapter created (da)
'specify prefix and suffix for cb
Dim cb = New OleDbCommandBuilder(da) With {
.QuotePrefix = "[",
.QuoteSuffix = "]"
}
'use try catch statement to fill the datatable (dt) using the dataadapter (da)
Try
da.Fill(dt)
Catch ex As Exception
MsgBox(Convert.ToString(ex))
End Try
'set the datasource of the datagridview to the datatable
dgv.DataSource = dt.DefaultView
'close the connection to the database
con.Close()
Go to your Back-End Access DB file. File > Options > Client Settings. For your Use Case No Locks should be fine, but Edited record setting will work as well if you need it
but its [sic] the only one I've got right now
Actually, it's not.
Have a look at SQL Server Compact. It's free, it's small and it handles multiple users with aplomb.
You can add all the references you need using NuGet.

Vb.net Could not save, locked by another user

I am having the problem that about when i try to update a picture, and i click save the error message said that "could not save, locked by another user. where error i did? Sometimes I could update, but sometimes not, why?
And I realize that I have to open Access file and close it then will work.
The error line is cmd.ExecuteNonQuery().
Dim con = New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source =..\room.accdb")
Dim ms As New MemoryStream
Dim arrimg As Byte()
Me.PictureBox1.Image.Save(ms, Imaging.ImageFormat.Png)
arrimg = ms.GetBuffer()
ms.Read(arrimg, 0, ms.Length)
Dim sql As String = "UPDATE Userss SET profilepicture =#profilepicture WHERE studentid=" & Form1.txtStuID.Text & ";"
Dim cmd As New OleDbCommand
con.open()
cmd = New OleDbCommand(sql, con)
Dim photo As OleDbParameter = New OleDbParameter("#profilepicture", SqlDbType.Image)
photo.Value = arrimg
cmd.Parameters.Add(photo)
cmd.ExecuteNonQuery()
MessageBox.Show("Profile picture saved.")
con.close()
The 'locked by another user' is a vague message by MS Access since it can be due to a couple of different reasons. It's not to be confused by a specific reason (such as always meaning a table lock). It can be that or it can be the *.ldb file itself or it can be form/table design or even permissions related. However, in your scenario, you are getting it since you have opened the db on MS Access as you mentioned. Opening the db / tables, usually put a lock on the tables.
When you open a record or the form (or table structure) is not designed optimally or someone opens the mdb/accdb who doesn't have both read and write permissions to the folder and file. This error has to do with the *.ldb file associated with the *.mdb/accdb file which is actually what is 'locking' and causing the error.
Some options can be considered, like;
using a NO LOCK in your update statement, but that might not be the best one.
make sure all your connections are well disposed after use.
use using blocks for your objects that implement IDisposable.
you can also read a bit about DEADLOCKS, it might help avoid locks
This should work:
Dim arrimg As Byte()
Using ms As New MemoryStream
Me.PictureBox1.Image.Save(ms, Imaging.ImageFormat.Png)
arrimg = ms.GetBuffer()
ms.Read(arrimg, 0, ms.Length)
End Using
Using con As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source =..\room.accdb")
con.Open()
Using cmd As New OleDbCommand("UPDATE Userss SET profilepicture = #profilepicture WHERE studentid = #studentId;", con)
Dim studentID As New OleDbParameter("#studentId", Form1.txtStuID.Text)
cmd.Parameters.Add(studentID)
Dim photo As New OleDbParameter("#profilepicture", SqlDbType.Image)
photo.Value = arrimg
cmd.Parameters.Add(photo)
cmd.ExecuteNonQuery()
MessageBox.Show("Profile picture saved.")
End Using
con.Close()
End Using
From the code i can see you mix presentation and data access logic, you should probably limit each of your feature to DAL methods so it's easier to find where your DB connection stay open.
With Using like this you can easily avoid to manually dispose the objects and you can be sure the connection will be closed even with exceptions.
Always use parameters and check for disposable object (like MemoryStream).

Uploading varbinary using ADODB.Stream - how to use LoadFromFile if file is open on computer for inserting into SQL varbinary(max) field?

Goal
I am attempting to do the following from Microsoft Access:
Insert into a SQL 2008 database a varbinary(max) field
Read binary file (Excel/Word)
Insert into SQL database
This seems like it would be straightforward.
Background
It would appear ADODB.Stream is the best way to do this. I am not convinced this is best. Filestream is disabled on my SQL server environment.
I have written code which will accomplish what I want in VBA for all files which are not opened. I do not want to require users to close the files prior to reading them with VBA.
This is my current code:
Dim mStream As ADODB.Stream
Set mStream = New ADODB.Stream
Dim fName As String
fName = "C:\tmp.docx"
mStream.Type = adTypeBinary
mStream.Open
mStream.LoadFromFile fName 'this fails if the file is open
Dim mCmd As ADODB.Command
Set mCmd = New ADODB.Command
With mCmd
' define query
.CommandText = "INSERT INTO testingvarbinary (testVarBinary) " & _
"VALUES (?)"
.CommandType = adCmdText
' add parameter
.Parameters.Append .CreateParameter("#P1", adVarBinary, adParamInput, mStream.Size, mStream.Read)
End With
mCmd.ActiveConnection = CurrentProject.Connection
mCmd.Execute
I've thought about trying to simply copy the file to C:\tmp or something and read that one, but it just seems like there should be a way to do this.
Question
How can I modify the above code to work for files which a user has opened? In this thread it seems there is a way to use the .Open command to do this, but I've been unsuccessful in even getting the .Open to return ANYTHING let alone the entire file. I've not found a single example using Open rather than LoadFromFile for an actual file on my computer.
If there is a different way in VBA than using ADODB.Stream that would be great too, I'm not tied to using it at all.