As a quick background, I'm a beginner at database programming so forgive me if anything that I post here doesn't make sense or is outright dumb.
I'm trying to make a WinForm application that will allow users to connect to a .mdb Access 97 database of their choosing, allow the user to make changes (e.g. add new data rows on WinForm which will then be applied to the original .mdb database), and sync those changes across different .mdb Access 97 databases if desired.
To give you an idea of what I have so far. I have successfully connected to a .mdb Access 97 database using C# and I output the database table contents into a DataSet object from which I then dumped into a C1FlexGrid (code will follow momentarily). Now before anyone mentions anything, I cannot upgrade the database file to a newer version, so it has to stay as an Access 97 version file.
Here is the code I used to connect:
dbConnection = new OleDbConnection(#"Provider=Microsoft.JET.OLEDB.4.0;Data Source=" + path);
dbCommand = new OleDbCommand("SELECT * from MAIN", dbConnection);
dbDataAdapter = new OleDbDataAdapter(dbCommand);
dbDataSet = new DataSet("MasterLanguageDB");
dbConnection.Open();
dbConnectionIsOpen = true;
// connection is successful, unlock connected mode features
EnterDatabaseConnectedMode();
dbDataAdapter.Fill(dbDataSet);
flexGrid.DataSource = dbDataSet.Tables[0];
My question is this: is the C1FlexGrid that I now have populated with the .mdb database file contents "binded" to the .mdb file? Because 1, I didn't bind the datasource the way Microsoft suggests doing it by way of the Add Data Source wizard in Visual Studio 2008 (because the user can connect to any .mdb database they choose -- not just one) and 2, I want whatever changes I make to the C1FlexGrid to apply to the original database.
If the answer is no, how do I create that "binding" or add the rows to the database?
The ADO.NET classes like Dataset or DataTable are disconnected objects.
Meaning that they can't directly update the database.
The OleDbDataAdapter has a method called Update that takes care to send all the modified data to the database.
So, supposing you have a button somewhere to save your changes, then you need to call in the click event
dbDataAdapter.Update(dbDataSet);
However this requires that you keep the object instances at the class global level
Another point is the need to prepare the dbDataAdapter.InsertCommand, dbDataAdapter.UpdateCommand and dbDataAdapter.DeleteCommand.
These commands could be created simply using an instance of OleDbCommandBuilder just after you have set the SelectCommand
dbDataAdapter = new OleDbDataAdapter(dbCommand);
OleDbCommandBuilder cb = new OleDbCommandBuilder(dbDataAdapter);
Related
I am working on a C# project with a Microsoft SQL Server database.
This is how I have connected the database:
DataSet ds_display = new DataSet();
SqlDataAdapter da_display = new SqlDataAdapter("Select * from Files", "Data Source=DESKTOP-21EVLOU\\SQLEXPRESS;Initial Catalog=UPM;Integrated Security=True");
da_display.Fill(ds_display, "Files");
dataGridView1.DataSource = ds_display.Tables["Files"].DefaultView;
The code works fine for all operations including reading data, writing data, updating data and deleting data as well. However, it works only when each operation is on individual form, not on a single form.
I have created several different panels and show/hide them based on the respective button. Each panel has its own operation and Data Grid View. The display option works fine but the addition, updating and deleting operations do nothing.
Can you please help identify the issue here?
I'm working on a pretty special, legacy project where I need to build an app for PDA devices under Windows Mobile 6.5. The devices have a local database (SQL Server CE) which we are supposed to sync with a remote database (Microsoft Access) whenever they are docked and have network access.
So the local database using SQL Server CE works fine, but I can’t figure out a way to sync it to the Access database properly.
I read that ODBC and OLEDB are unsupported under Windows Mobile 6.5, most ressources I find are obsolete or have empty links, and the only way I found was to export the local database relevant tables in XML in the hope to build a VBA component for Access to import them properly. (and figure out backwards sync).
Update on the project and new questions
First of all, thanks to everyone who provided an useful answer, and to #josef who saved me a lot of time with the auto path on this thread.
So a remote SQL Server is a no go for security reasons (client is paranoid about security and won't provide me a server). So I'm tied to SQL Server CE on the PDA and Access on the computer.
As for the sync:
The exportation is fine: I'm using multiple dataAdapters and a WriteXML method to generate XML files transmitted by FTP when the device is plugged back in. Those files are then automatically imported into the Access database. (see code at the end).
My problem is on the importation: I can acquire data through XML readers from an Access-generated file. This data is then inserted in a dataset (In fact, I can even print the data on the PDA screen) but I can't figure out a way to do an "UPSERT" on the PDA's database. So I need a creative way to update/insert the data to the tables if they already contains data with the same id.
I tried two methods, with SQL errors (from what I understood it's SQL Server CE doesn't handle stored procedures or T-SQL). Example with a simple query that is supposed to update the "available" flag of some storage spots:
try
{
SqlCeDataAdapter dataAdapter = new SqlCeDataAdapter();
DataSet xmlDataSet = new DataSet();
xmlDataSet.ReadXml(localPath +#"\import.xml");
dataGrid1.DataSource = xmlDataSet.Tables[1];
_conn.Open();
int i = 0;
for (i = 0; i <= xmlDataSet.Tables[1].Rows.Count - 1; i++)
{
spot = xmlDataSet.Tables[1].Rows[i].ItemArray[0].ToString();
is_available = Convert.ToBoolean(xmlDataSet.Tables[1].Rows[i].ItemArray[1]);
SqlCeCommand importSpotCmd = new SqlCeCommand(#"
IF EXISTS (SELECT spot FROM spots WHERE spot=#spot)
BEGIN
UPDATE spots SET available=#available
END
ELSE
BEGIN
INSERT INTO spots(spot, available)
VALUES(#spot, #available)
END", _conn);
importSpotCmd.Parameters.Add("#spot", spot);
importSpotCmd.Parameters.Add("#available", is_available);
dataAdapter.InsertCommand = importSpotCmd;
dataAdapter.InsertCommand.ExecuteNonQuery();
}
_conn.Close();
}
catch (SqlCeException sql_ex)
{
MessageBox.Show("SQL database error: " + sql_ex.Message);
}
I also tried this query, same problem SQL server ce apparently don't handle ON DUPLICATE KEY (I think it's MySQL specific).
INSERT INTO spots (spot, available)
VALUES(#spot, #available)
ON DUPLICATE KEY UPDATE spots SET available=#available
The code of the export method, fixed so it works fine but still relevant for anybody who wants to know:
private void exportBtn_Click(object sender, EventArgs e)
{
const string sqlQuery = "SELECT * FROM storage";
const string sqlQuery2 = "SELECT * FROM spots";
string autoPath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase); //get the current execution directory
using (SqlCeConnection _conn = new SqlCeConnection(_connString))
{
try
{
SqlCeDataAdapter dataAdapter1 = new SqlCeDataAdapter(sqlQuery, _conn);
SqlCeDataAdapter dataAdapter2 = new SqlCeDataAdapter(sqlQuery2, _conn);
_conn.Open();
DataSet ds = new DataSet("SQLExport");
dataAdapter1.Fill(ds, "stock");
dataAdapter2.Fill(ds, "spots");
ds.WriteXml(autoPath + #"\export.xml");
}
catch (SqlCeException sql_ex)
{
MessageBox.Show("SQL database error: " + sql_ex.Message);
}
}
}
As Access is more or less a stand-alone DB solution I strongly recommend to go with a full flavored SQL Server plus IIS to setup a Merge Replication synchronisation between the SQL CE data and the SQL Server data.
This is described with full sample code and setup in the book "Programming the .Net Compact Framework" by Paul Yao and David Durant (chapter 8, Synchronizing Mobile Data).
For a working sync, all changes to defined tables and data on the server and the CE device must be tracked (done via GUIDs, unique numbers) with there timestamps and a conflict handling has to be defined.
If the data is never changed by other means on the server, you may simply track Device side changes only and then push them to the Access database. This could be done by another app that does Buld Updates like described here.
If you do not want to go the expensive way to SQL Server, there are cheaper solutions with free SQLite (available for CE and Compact Framework too) and a commercial Sync tool for SQLite to MSAccess like DBSync.
If you are experienced, you may create your own SQLite to MS ACCESS sync tool.
I'm developing a C# application and I want to copy a whole table from a SQL Server CE database to another programmatically. I know I can use this command in SQL Server, but I'm not sure how to use two database connections in one query in C#.
Select * into DestinationDB.dbo.tableName from SourceDB.dbo.SourceTable
Thanks in advance.
You wouldn't do it the same way as in SQL Server because there's no single server that manages both databases. Your app is the only thing that links the two databases so the the data has to go through your app. You're trying to do it the wrong way and that's why you can't find a solution: you're looking for the wrong thing.
There are examples of this out there. I know, because I've written more than one myself. You simply need to use a data adapter to populate a DataTable from the first database and then a data adapter to save the contents of that DataTable to the second database. If the data sources are the same type then you can even use just one data adapter because the SelectCommand and InsertCommand can have different connections.
using (var adapter = new SqlDataAdapter("SELECT statement here", "source connection string here"))
using (var destinationConnection = new SqlConnection("destination connection string here"))
using (var insertCommand = new SqlCommand("INSERT statement here", destinationConnection))
{
// Add parameters to insertCommand here.
adapter.InsertCommand = insertCommand;
// Leave the RowState of all DataRows as Added so they are ready to be inserted.
adapter.AcceptChangesDuringFill = false;
var table = new DataTable();
// Retrieve data from source and save to destination.
adapter.Fill(table);
adapter.Update(table);
}
That example uses SqlClient but it works the same for any provider, including SqlServerCe.
I'm taking a class in C# web dev and I'm currently trying to display data from a .mdb (Microsoft Access) database. I have two pages that both want to show a table stored in the database in a grid view. One page's form uses function that connects the the db and then writes to the table to the grid. it connects with this call:
sqlConn = new OleDbConnection("PROVIDER=Microsoft.Jet.OLEDB.4.0;" +
"Data Source=" + Database);
sqlDA = new OleDbDataAdapter("select * from tblPersonnel where LastName = '"+strSearch+"'", sqlConn);
//Initialize a new activity
DS = new dsPersonnel();
//Add rows to the dataset from the data source
sqlDA.Fill(DS.tblPersonnel);
The database is called PayrollSystem_DB.mdb. In the other page I placed a SqlDataSource control on the page and connected it to PayrollSystem_DB.mdb. Then set this to the gridView. However when running, the the first page shows a long table with data I've entered since last week. and the 2nd page shows only data from the first day I tested it. Looking at the db in the vs explorer the tables are identical to teh second page.
So I'm wondering were all the other data is being stored that the first page is showing? Am I unknowingly using a database I can't see in the solution?
Just a guess, but have you tried clearing your browsers cache and then looking at the first page again? Try that. If the data is still showing I would compare the size of the MDB before and after an insert to see if the MDB grows. If not, try a search of all the files on your PC limiting it to only files modified recently, I think that will at least help you find where the data is being stored.
I have a winforms app that uses sqlite to store data. Instead of shipping a blank database, can I use scripts to create the tables the first time the user uses the app? Can you point to a C# example?
Update: I want to avoid shipping a blank database. So if a user install the app for 1 user only, only his profile gets a copy. All users profile gets the database if the install is for all users.
Yes, this is possible:
When the application first runs, check if the database file exists.
If it doesn’t, open it with the Sqlite option FailIfMissing=False. This will create a new file.
Then, use SQL commands like CREATE TABLE ... to create the schema structure.
For the second step, I use code that looks something like this:
public DbConnection CreateConnectionForSchemaCreation(string fileName)
{
var conn = new SQLiteConnection();
conn.ConnectionString = new DbConnectionStringBuilder()
{
{"Data Source", fileName},
{"Version", "3"},
{"FailIfMissing", "False"},
}.ConnectionString;
conn.Open();
return conn;
}