SQL Server CE two way sync with remote Access database - c#

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.

Related

SqlDataAdapter.Fill populate no rows in given DataTable

all.
Here is my code.
private void FillData(DataTable data, FilterModel filter){
var cmdStr = MySqlCmdGenerator.Generate(filter);
using (var cmd = new SqlCommand(cmdStr, connection_))
using (var da = new SqlDataAdapter(cmd)){
da.Fill(data);
}
}
public DataTable GetData(IEnumerable<FilterModel> filters){
var data = new DataTable("mydata");
foreach (var filter in filters){
FillData(data);
}
return data;
}
It's a very simple code, which just use SqlDataAdapter to Fill datatable according to the filters.
The problem is that sometimes GetData method returns empty datatable.
First I execute the generated query on SQL Server Management Studio, and it returns correct result (it contains about 100,000 records).
Then I have used Sql Server Profiler to profile what happened in server, and get some errors:
Sort Warnings
RPC:Completed (TextData: Query with a given filter)
Hash Warning
Sort Warnings
RPC:Completed (TextData: Query with a given filter)
User Error Message (TextData: The statement has been terminated.)
Attention
Audit Logout
RPC:Completed (TextData: sp_reset_connection)
(occurs in this order)
After google the error but I could not figure out what is exactly happen in server and application.
Any advice will be appreciated.
Thanks!
Differences between results from a .NET application and those from SQL Server Management Studio are often due to different configuration of ANSI settings for the SQL Server connection.
This assumes you've eliminated the obvious error causes, e.g. you're inadvertently connection to different databases from the two environments.
You can see the settings used by SQL Server Management Studio in Tools / Options / Query Execution / SQL Server / ANSI. On my installation they default to:
SET ANSI_NULL_DFLT_ON ON
SET ANSI_PADDING ON
SET ANSI_NULLS ON
I suggest you try switching these OFF one by one in Management Studio to attempt to reproduce the configuration used by .NET. Once you understand what's happening, you can do one of:
Add SET statements to your .NET query
Modify your query so it isn't dependent on these settings (may need to add ISNULL or explicit tests for NULL).

Adding New Row Data from C1FlexGrid to Access 97 .mdb Database C#

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);

What is the best way for me to connect to a offline database through C#

I have an application that needs to store loads of data in a table format. I want something easy to configure, which is also in built with C#.NET. I don't want to have to include additional DLL files.
Also some links to tutorials, explaining the connection process and querying would be great. I'm assuming this is just like PHP, but which database type do I need?
It needs to be able to hold a lot of data and the ability to perform backups would be nice.
I'm not sure what you mean by "built in with C#.NET", but SQL Server Express comes with Visual Studio.
If you're looking for "a self-contained, embeddable, zero-configuration SQL database engine", you could try System.Data.SQLite.
If you want an offline database you could use SQL Server CE, as its a in-process database that does not require being attached to a server instance, which is really what you want then. Here is an example in C# on how you would connect, and populate a data table to manipulate some data.
// this connectionstring can also be an absolute file path
string connectionString = "Data Source=|DataDirectory|\mydatabase.sdf";
using (SqlCeConnection connection = new SqlCeConnection(connectionString)) {
try {
connection.Open();
}
catch (SqlCeException) {
// connection failed
}
using (SqlCeDataAdapter adapter = new SqlCeDataAdapter("SELECT * FROM <table>", connection)) {
using (DataTable table = new DataTable("<table>")) {
adapter.Fill(); // Populate the table with your select statement
// do stuff with the datatable
// example:
foreach (DataRow row in table.Rows) {
row["mycolumn"] = "somedata";
}
table.AcceptChanges();
}
}
}
You can even use commands instead of data tables
using (SqlCeCommand command = new SqlCeCommand("DELETE FROM <table> WHERE id = '0'", connection)) {
command.ExecuteNonQuery(); // executes command
}
Have a look at the ease of SQL Server Compact
Not build-in but easily added, no install and free.

C#: DB2 test available connection first

I have a C# .NET program running an ETL which connects to a DB2 database. Sometimes this database is down, so I'd like to do a health check at the beginning of the application to see if the database is available, without actually calling any stored procedures or pushing any data. Here's an example of the code I'm using now:
OdbcConnection myODBCConnection = new OdbcConnection("DSN=DB2AA;UID=ABCD;PWD=1234;");
OdbcCommand myODBCCommand = new OdbcCommand();
myODBCCommand.CommandType = CommandType.StoredProcedure;
myODBCCommand.CommandText = "{CALL SYSPROC.ABC001(?, ?)}";
myODBCCommand.Parameters.Add("INPUT", OdbcType.VarChar, 500);
myODBCCommand.Parameters["INPUT"] = myString
myODBCCommand.Connection = myODBCConnection
myODBCConnection.Open();
OdbcTransaction myTrans;
myTrans = myODBCConnection.BeginTransaction();
myODBCCommand.Transaction = myTrans;
myTrans.Commit();
myODBCCommand.ExecuteNonQuery();
myODBCConnection.Close();
What's the best way to test this connection without actually pushing any data?
You can simply run some innoccuous select query to check to see if the db is available.
You can try to do something as simple as:
Select 1
Or
Select getdate()
Those simple queries don't even touch any tables but will return only if the rdbms is running.
Note: those examples are for sql server but might work for db2. I haven't had to do a live check on a db2 yet though the similar concept should be doable.
Note 2: after a closer look at your code, all you should really have/need to do is check for success of your odbc connection's .Open() call.

Paradox Tables in C#

I'm trying to read a Paradox 5 table into a dataset or simular data structure with the view to putting it into an SQL server 2005 table. I've trawled google and SO but with not much luck. I've tried ODBC:
public void ParadoxGet()
{
string ConnectionString = #"Driver={Microsoft Paradox Driver (*.db )};DriverID=538;Fil=Paradox 5.X;DefaultDir=C:\Data\;Dbq=C:\Data\;CollatingSequence=ASCII;";
DataSet ds = new DataSet();
ds = GetDataSetFromAdapter(ds, ConnectionString, "SELECT * FROM Growth");
foreach (String s in ds.Tables[0].Rows)
{
Console.WriteLine(s);
}
}
public DataSet GetDataSetFromAdapter(DataSet dataSet, string connectionString, string queryString)
{
using (OdbcConnection connection = new OdbcConnection(connectionString))
{
OdbcDataAdapter adapter = new OdbcDataAdapter(queryString, connection);
connection.Open();
adapter.Fill(dataSet);
connection.Close();
}
return dataSet;
}
This just return the error
ERROR [HY000] [Microsoft][ODBC Paradox Driver] External table is not in the expected format.
I've also tired OELDB (Jet 4.0) but get the same External table is not in the expected format error.
I have the DB file and the PX (of the Growth table) in the Data folder... Any help would be much appriciated.
I've had the same error. It appeared when I started my C# project on Win2008 64 (previos OS was Win2003 32). Also I found out that it worked fine in console apps and gave different errors in winforms. It seems that problem comes from the specifics of 32 ODBC driver working on 64-bit systems.
My solution was:
// Program.cs
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// it is important to open paradox connection before creating
// the first form in the project
if (!Data.OpenParadoxDatabase())
return;
Application.Run(new MainForm());
}
The connectionstring is common:
string connStr = #"Driver={{Microsoft Paradox Driver (*.db )}};DriverID=538;
Fil=Paradox 7.X;DefaultDir=C:\\DB;Dbq=C:\\DB;
CollatingSequence=ASCII;";
After opening connection you may close it in any place after creating first Form (if you need to keep DB closed most of time), for example:
private void MainForm_Load(object sender, EventArgs e)
{
Data.CloseParadoxDatabase();
}
After doing that you may open and close connection every time you want during execution of your application and you willn't get any exceptions.
Maybe this will help you out,
http://support.microsoft.com/support/kb/articles/Q237/9/94.ASP?LN=EN-US&SD=SO&FR=1
http://support.microsoft.com/support/kb/articles/Q230/1/26.ASP
It appears that the latest version of the Microsoft Jet Database Engine
(JDE) does not fully support Paradox unless the Borland Database Engine
(BDE) is also installed.
Try to Run all The Applications with the "Run As Administrator" privileges especially run the VS.NET with "Run As Administrator "... and I am sure your problem get solved
This isn't an answer, but more of a question: any particular reason you're trying to use C# to do the data manipulation as opposed to using SQL Server tools to load the data directly? Something like DTS or SSIS would seem like a better tool for the job.
Thanks, I'll give that a try. I wanted to use C# so I can put it on some web pages without the extra set of putting it in SQL server.

Categories