Проблема C #: Какой самый простой способ загрузить файл .MDB, внести в него изменения и сохранить изменения в исходном файле? - PullRequest
5 голосов
/ 06 февраля 2009

Мой проект, над которым я работаю, почти завершен. Я загружаю файл .MDB, отображаю содержимое в DataGrid и пытаюсь получить эти изменения в DataGrid и сохранить их обратно в файл .MDB. Я также собираюсь создать функцию, которая позволит мне взять таблицы из одного файла .MDB и сохранить его в другом файле .MDB. Конечно, я не могу ничего сделать, если не могу понять, как сохранить изменения обратно в файл .MDB.

Я тщательно исследовал Google, и на мой вопрос нет ответов. Я считаю себя новичком в этой конкретной теме, поэтому, пожалуйста, не усложняйте ответы - мне нужен самый простой способ отредактировать файл .MDB! Пожалуйста, предоставьте примеры программирования.

  1. Предположим, что я уже установил соединение с DataGrid. Как я могу получить изменения, сделанные Datagrid? Я уверен, что этот вопрос достаточно прост, чтобы ответить.
  2. Затем мне нужно знать, как взять этот набор данных, вставить его в набор данных, из которого он получен, затем взять этот набор данных и переписать файл .MDB. (Если есть способ только вставить таблицы, которые были изменены, я бы предпочел это.)

Заранее спасибо, дайте мне знать, если вам нужна дополнительная информация. Это последнее, что мне, вероятно, придется спросить об этой теме ... слава богу.

EDIT:

.mdb, с которой я работаю, это База данных Microsoft Access. (я даже не знал, что было несколько файлов .mdb)

Я знаю, что не могу записать напрямую в файл .MDB через потоковую запись или что-то еще, но есть ли способ, которым я могу сгенерировать файл .MDB с уже имеющейся информацией DataSet? ИЛИ есть ли способ добавить таблицы в файл .MDB, который я уже загрузил в DataGrid. Должен быть способ!

Опять же, мне нужен способ сделать это ПРОГРАММНО в C #.

EDIT:

Хорошо, мой проект довольно большой, но я использую отдельный файл класса для обработки всех соединений с БД. Я знаю, что мой дизайн и источник действительно неаккуратный, но он выполняет свою работу. Я так же хорош, как примеры, которые я нахожу в Интернете.

Помните, я просто подключаюсь к DataGrid в другой форме. Дайте мне знать, если вам нужен мой код из формы Datagrid (хотя я не знаю, зачем он вам понадобится). DatabaseHandling.cs обрабатывает 2 файла .MDB. Таким образом, вы увидите два набора данных там. Я буду использовать это в конечном итоге, чтобы взять таблицы из одного набора данных и поместить их в другой набор данных. Мне просто нужно выяснить, как сохранить эти значения НАЗАД в файл .MDB.

Есть ли в любом случае, чтобы сделать это? Должен быть способ ...

EDIT:

Из того, что я исследовал и прочитал ... Я думаю, что ответ прямо у меня под носом. С помощью команды «Обновить ()». Теперь, когда это подтверждает, что существует простой способ сделать это, я все еще остаюсь с проблемой, что у меня нет никакой подсказки, как использовать эту команду обновления.

Возможно, я смогу настроить это так:

Oledb.OledbConnection cn = new Oledb.OledbConnection(); 
cn.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Staff.mdb"; 
Oledb.OledbCommand cmd = new Oledb.OledbCommand(cn); 
cmd.CommandText = "INSERT INTO Customers (FirstName, LastName) VALUES (@FirstName, @LastName)"; 

Я думаю, что это может сделать, но я не хочу вставлять что-либо вручную. Я хочу сделать оба из них вместо:

  • Возьмите информацию, которая изменена в Datagrid, и обновите файл базы данных Access (.mdb), который я получил от
  • Создайте функцию, которая позволит мне брать таблицы из другого файла базы данных Access (.mdb) и заменять их во вторичном файле базы данных Access (.mdb). Оба файла будут использовать одну и ту же структуру, но в них будет разная информация.

Я надеюсь, что кто-то придумает ответ на этот вопрос ... мой проект завершен, все, что ждет, - это один простой ответ.

Заранее еще раз спасибо.

EDIT:

Хорошо ... хорошие новости. Я выяснил, как запросить сам файл .mdb (я думаю). Вот код, который не работает, потому что я получаю ошибку во время выполнения из-за команды sql, которую я пытаюсь использовать. Что приведет меня к следующему вопросу.

Добавлен новый код функции в DatabaseHandling.cs:

static public void performSynchronization(string table, string tableTwoLocation)
{
    OleDbCommand cmdCopyTables = new OleDbCommand("INSERT INTO" + table + "SELECT * FROM [MS Access;" + tableTwoLocation + ";].[" + table + "]"); // This query generates runtime error
    cmdCopyTables.Connection = dataconnectionA;
    dataconnectionA.Open();
    cmdCopyTables.ExecuteNonQuery();
    dataconnectionA.Close();
}

Как видите, мне действительно удалось выполнить запрос к самому соединению, которое, как я считаю, является действительным файлом Access .MDB. Как я уже сказал, SQL-запрос, который я выполнил для файла, не работает и выдает ошибку во время выполнения.

Команда, которую я пытаюсь выполнить, должна взять таблицу из файла .MDB и перезаписать таблицу того же типа, что и другой файл .MDB. Команда SQL, которую я пытался описать выше, пыталась напрямую взять таблицу из файла .mdb и напрямую поместить ее в другой файл - это не то, что я хочу сделать. Я хочу взять всю информацию из файла .MDB - поместить таблицы в Datatable, а затем добавить все Datatables в набор данных (что я и сделал). Я хочу сделать это для двух файлов .MDB. После того, как у меня есть два набора данных, я хочу взять определенные таблицы из каждого набора данных и добавить их в каждый файл следующим образом:

  • DataSetA >>>> ----- [Добавить таблицы (Перезаписать их)] ----- >>>> DataSetB
  • DataSetB >>>> ----- [Добавить таблицы (Перезаписать их)] ----- >>>> DataSetA

Я хочу взять каждый из этих наборов данных и затем поместить их НАЗАД в каждый файл Access .MDB, из которого они пришли. По сути, синхронизация обеих баз данных.

Итак, мои вопросы, пересмотренные, таковы:

  1. Как создать запрос SQL, который добавит таблицу в файл .MDB, перезаписав существующую с тем же именем. Запрос должен иметь возможность динамически создаваться во время выполнения с помощью массива, который заменяет переменную именем таблицы, которую я хочу добавить.
  2. Как получить изменения, сделанные Datagrid, в DataTable и поместить их обратно в DataTable (или DataSet), чтобы я мог отправить их в файл .MDB?

Я пытался уточнить как можно больше ... потому что я считаю, что я не очень хорошо объясняю свою проблему. Теперь этот вопрос стал слишком длинным. Я просто хотел бы объяснить это лучше. : [

EDIT:

Спасибо пользователю ниже, я думаю, что я почти нашел исправление - ключевое слово почти . Вот мой обновленный код DatabaseHandling.cs ниже. Я получаю ошибку времени выполнения "Несоответствие типов данных". Я не знаю, как это возможно, учитывая, что я пытаюсь скопировать эти таблицы в другую базу данных с точно такой же настройкой.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.OleDb;
using System.Data;
using System.IO;

    namespace LCR_ShepherdStaffupdater_1._0
    {
        public class DatabaseHandling
        {
            static DataTable datatableB = new DataTable();
            static DataTable datatableA = new DataTable();
            public static DataSet datasetA = new DataSet();
            public static DataSet datasetB = new DataSet();
            static OleDbDataAdapter adapterA = new OleDbDataAdapter();
            static OleDbDataAdapter adapterB = new OleDbDataAdapter();
            static string connectionstringA = "Provider=Microsoft.Jet.OLEDB.4.0;" + "Data Source=" + Settings.getfilelocationA();
            static string connectionstringB = "Provider=Microsoft.Jet.OLEDB.4.0;" + "Data Source=" + Settings.getfilelocationB();
            static OleDbConnection dataconnectionB = new OleDbConnection(connectionstringB);
            static OleDbConnection dataconnectionA = new OleDbConnection(connectionstringA);
            static DataTable tableListA;
            static DataTable tableListB;

            static public void addTableA(string table, bool addtoDataSet)
            {
                dataconnectionA.Open();
                datatableA = new DataTable(table);
                try
                {
                    OleDbCommand commandselectA = new OleDbCommand("SELECT * FROM [" + table + "]", dataconnectionA);
                    adapterA.SelectCommand = commandselectA;
                    adapterA.Fill(datatableA);
                }
                catch
                {
                    Logging.updateLog("Error: Tried to get " + table + " from DataSetA. Table doesn't exist!");
                }

                if (addtoDataSet == true)
                {
                    datasetA.Tables.Add(datatableA);
                    Logging.updateLog("Added DataTableA: " + datatableA.TableName.ToString() + " Successfully!");
                }

                dataconnectionA.Close();
            }

            static public void addTableB(string table, bool addtoDataSet)
            {
                dataconnectionB.Open();
                datatableB = new DataTable(table);

                try
                {
                    OleDbCommand commandselectB = new OleDbCommand("SELECT * FROM [" + table + "]", dataconnectionB);
                    adapterB.SelectCommand = commandselectB;
                    adapterB.Fill(datatableB);
                }
                catch
                {
                    Logging.updateLog("Error: Tried to get " + table + " from DataSetB. Table doesn't exist!");
                }



                if (addtoDataSet == true)
                {
                    datasetB.Tables.Add(datatableB);
                    Logging.updateLog("Added DataTableB: " + datatableB.TableName.ToString() + " Successfully!");
                }

                dataconnectionB.Close();
            }

            static public string[] getTablesA(string connectionString)
            {
                dataconnectionA.Open();
                tableListA = dataconnectionA.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, new Object[] { null, null, null, "TABLE" });
                string[] stringTableListA = new string[tableListA.Rows.Count];

                for (int i = 0; i < tableListA.Rows.Count; i++)
                {
                    stringTableListA[i] = tableListA.Rows[i].ItemArray[2].ToString();
                }
                dataconnectionA.Close();
                return stringTableListA;
            }

            static public string[] getTablesB(string connectionString)
            {
                dataconnectionB.Open();
                tableListB = dataconnectionB.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, new Object[] { null, null, null, "TABLE" });
                string[] stringTableListB = new string[tableListB.Rows.Count];

                for (int i = 0; i < tableListB.Rows.Count; i++)
                {
                    stringTableListB[i] = tableListB.Rows[i].ItemArray[2].ToString();
                }
                dataconnectionB.Close();
                return stringTableListB;
            }

            static public void createDataSet()
            {

                string[] tempA = getTablesA(connectionstringA);
                string[] tempB = getTablesB(connectionstringB);
                int percentage = 0;
                int maximum = (tempA.Length + tempB.Length);

                Logging.updateNotice("Loading Tables...");
                for (int i = 0; i < tempA.Length ; i++)
                {
                    if (!datasetA.Tables.Contains(tempA[i]))
                    {
                        addTableA(tempA[i], true);
                        percentage++;
                        Logging.loadStatus(percentage, maximum);
                    }
                    else
                    {
                        datasetA.Tables.Remove(tempA[i]);
                        addTableA(tempA[i], true);
                        percentage++;
                        Logging.loadStatus(percentage, maximum);
                    }
                }

                for (int i = 0; i < tempB.Length ; i++)
                {
                    if (!datasetB.Tables.Contains(tempB[i]))
                    {
                        addTableB(tempB[i], true);
                        percentage++;
                        Logging.loadStatus(percentage, maximum);
                    }
                    else
                    {
                        datasetB.Tables.Remove(tempB[i]);
                        addTableB(tempB[i], true);
                        percentage++;
                        Logging.loadStatus(percentage, maximum);
                    }
                }


            }

            static public DataTable getDataTableA()
            {
                datatableA = datasetA.Tables[Settings.textA];

                return datatableA;
            }
            static public DataTable getDataTableB()
            {
                datatableB = datasetB.Tables[Settings.textB];
                return datatableB;
            }

            static public DataSet getDataSetA()
            {
                return datasetA;
            }

            static public DataSet getDataSetB()
            {
                return datasetB;
            }

            static public void InitiateCopyProcessA()
            {
                DataSet tablesA;
                tablesA = DatabaseHandling.getDataSetA();

                foreach (DataTable table in tablesA.Tables)
                {
                    CopyTable(table, connectionstringB);
                }
            }

            public static void CopyTable(DataTable table, string connectionStringB)
            {
                var connectionB = new OleDbConnection(connectionStringB);
                foreach (DataRow row in table.Rows)
                {
                    InsertRow(row, table.Columns, table.TableName, connectionB);
                }
            }

            public static void InsertRow(DataRow row, DataColumnCollection columns, string table, OleDbConnection connection)
            {
                var columnNames = new List<string>();
                var values = new List<string>();

                for (int i = 0; i < columns.Count; i++)
                {
                    columnNames.Add("[" + columns[i].ColumnName + "]");
                    values.Add("'" + row[i].ToString().Replace("'", "''") + "'");
                }

                string sql = string.Format("INSERT INTO {0} ({1}) VALUES ({2})",
                        table,
                        string.Join(", ", columnNames.ToArray()),
                        string.Join(", ", values.ToArray())
                    );

                ExecuteNonQuery(sql, connection);
            }

            public static void ExecuteNonQuery(string sql, OleDbConnection conn)
            {
                if (conn == null)
                    throw new ArgumentNullException("conn");

                ConnectionState prevState = ConnectionState.Closed;
                var command = new OleDbCommand(sql, conn);
                try
                {
                    prevState = conn.State;
                    if (prevState != ConnectionState.Open)
                        conn.Open();

                    command.ExecuteNonQuery(); // !!! Runtime-Error: Data type mismatch in criteria expression. !!!
                }
                finally
                {
                    if (conn.State != ConnectionState.Closed
                        && prevState != ConnectionState.Open)
                        conn.Close();
                }
            }

            }          
        }

Почему я получаю эту ошибку? Обе таблицы абсолютно одинаковы. Что я делаю неправильно? В худшем случае, как мне удалить таблицу в другом файле Access .MDB перед тем, как вставить точно такую ​​же таблицу структуры с другими значениями в ней?

Чувак, я бы хотел это выяснить ...

EDIT:

Хорошо, я прошел некоторое расстояние. Мой вопрос превратился в новый, и поэтому заслуживает отдельного обсуждения. Мне ответили на мой вопрос, так как теперь я знаю, как выполнять запросы непосредственно к открытому соединению. Спасибо всем!

Ответы [ 4 ]

3 голосов
/ 06 февраля 2009

Я не уверен, как далеко вы продвинулись, но если вам нужна операция быстрого перетаскивания, возможно, вы захотите взглянуть на создание строго типизированного набора данных, который соединяется, и на использование функций перетаскивания в Окно инструментов источников данных в Visual Studio.

Определенно есть образцы, но вы захотите.

  1. Создать новый набор данных
  2. Drag-n-Drop из дерева соединений данных в обозревателе серверов
  3. Создать новую форму
  4. Перетаскивание таблицы из инструмента источников данных
  5. Окно в форму.
  6. вуаля

Обновление:

Во-первых, я не на 100% понимаю вашу проблему. Если вы можете создать несколько LinkTable между файлами доступа, которые были бы наилучшими, вы можете скопировать данные между файлами с помощью оператора SQL, например «INSERT INTO Customers SELECT FirstName, LastName FROM File2.Customers». Если это не так и вариант, я думаю, вам придется зацикливать DataTables и вставлять записи вручную, используя операторы INSERT, аналогично вашему последнему редактированию. Что касается сетки данных, вам, вероятно, придется следить за тем, что изменилось, отслеживая событие RowChanged (не уверен, является ли это точным событием) или даже выполняют операторы вставки / обновления при изменении строки.

Обновление:

чтобы зациклить данные, вы бы сделали что-то вроде этого. не испытано. Я просто обновил это снова, чтобы включить функцию MakeValueDbReady. Это также не проверено, и я не уверен, правильно ли я обработал все случаи или даже все случаи. Вам действительно нужно будет отладить оператор sql и убедиться, что он генерирует правильное значение. Каждая база данных обрабатывает значения по-разному. По крайней мере, таким образом, значение parse извлекается. Я также понял, что вместо жесткого кодирования TableName вы сможете получить его из свойства DataTable

.
void CopyTable(DataTable table, string connectionStringB)
{
    var connectionB = new OleDbConnection(connectionStringB);
    foreach(DataRow row in table.Rows)
    {
        InsertRow(row, table.Columns, table.TableName, connectionB);
    }
}

public static void InsertRow(DataRow row, DataColumnCollection columns, string table, OleDbConnection connection)
{
    var columnNames = new List<string>();
    var values = new List<string>();

    // generate the column and value names from the datacolumns    
    for(int i =0;i<columns.Count; i++)
    {
        columnNames.Add("[" + columns[i].ColumnName + "]");
        // datatype mismatch should be fixed by this function
        values.Add(MakeValueDbReady(row[i], columns[i].DataType));
    }

    // create the sql
    string sql = string.Format("INSERT INTO {0} ({1}) VALUES ({2})",
            table,
            string.Join(", ", columnNames.ToArray()),
            string.Join(", ", values.ToArray())
        );

    // debug the accuracy of the sql here and even copy into 
    // a new Query in Access to test
    ExecuteNonQuery(sql, connection);
}

// as the name says we are going to check the datatype and format the value
// in the sql string based on the type that the database is expecting
public string MakeValueDbReady(object value, Type dataType)
{
    if (value == null)
        return null;

    if (dataType == typeof(string))
    {
        return "'" + value.ToString().Replace("'", "''") + "'"
    }
    else if (dataType == typeof(DateTime))
    {
        return "#" + ((DateTime)value).ToString + "#"
    }
    else if (dataType == typeof(bool))
    {
        return ((bool)value) ? "1" : "0";
    }

    return value.ToString();
}

public static void ExecuteNonQuery(string sql, OleDbConnection conn)
{
    if (conn == null)
        throw new ArgumentNullException("conn");

    ConnectionState prevState = ConnectionState.Closed;
    var command = new OleDbCommand(sql, conn);
    try
    {
        // the reason we are checking the prev state is for performance reasons
        // later you might want to open the connection once for the a batch
        // of say 500 rows  or even wrap your connection in a transaction.
        // we don't want to open and close 500 connections
        prevState = conn.State;
        if (prevState != ConnectionState.Open)
            conn.Open();

        command.ExecuteNonQuery();
    }
    finally
    {
        if (conn.State != ConnectionState.Closed
            && prevState != ConnectionState.Open)
            conn.Close();
    }
}
1 голос
/ 06 февраля 2009

Чтобы обновить исходный файл MDB с изменениями, внесенными в DataSet (не DataGrid, поскольку это просто UI поверх DataSet), просто используйте команду DataAdapter.Update .

Переместить таблицы с 1 на другое немного сложнее. Если таблица еще не существует в месте назначения, вам нужно создать ее с помощью оператора SQL CREATE . Затем DataAdapter.Fill a Набор данных из источника . Прокрутите каждую строку и установите ее состояние в RowAdded, вызвав DataRow.SetAdded . Затем передайте его обратно в DataAdapter.Update из базы данных destination .

РЕДАКТИРОВАТЬ: Код на следующий вопрос ....

0 голосов
/ 06 февраля 2009

Как вы подключаетесь к базе данных (файл .mdb)? Не могли бы вы опубликовать пример кода? Если вы подключаетесь к нему правильно, любые выполняемые с ним операции SQL должны автоматически сохраняться в базе данных.

Поэтому после подключения к базе данных вы можете выполнить SQL, который будет создавать таблицы, вставлять / обновлять / извлекать данные и т. Д. Попытка создания файла .mdb вручную не рекомендуется.

Вот пример:

http://www.java2s.com/Code/CSharp/Database-ADO.net/Access.htm

0 голосов
/ 06 февраля 2009

На самом деле существует более одного формата файла с расширением .mdb. Так что, если я угадаю неправильный, это будет неправильный ответ. Но это звучит как проблема Microsoft Access.

Вы не пишете напрямую в файл MDB. Они зашифрованы и сжаты. Самый простой способ изменить файл MDB - это загрузить его через Access и скопировать таблицы с помощью предоставленных методов.

...