Расскажу все сначала. Имеется БД в Access
(
24 таблицы,
все теблицы имеют ключ - поле с именем ID (Autoincrement),
все таблицы связаны друг с другом (каскадное удаление/обновление)
)
В приложении (WinForms) я хочу загузить всю БД из файла в DataSet поработать с DataSet'ом и сохранить изменения обратно в файл (структура DataSet'а полностью такая же как и у оригинальной БД - со всеми связями и ключами).
БД из файла в ДатаСет гружу следующим образом:
открываю соединение,
каждая таблица загружается отдельно своим ДатаАдаптером,
для каждого ДатаАдаптера создается КоммандБилдер,
подписываюсь на RowUpdated Адаптера, чтобы корректировать поле ID, когда таблица будет обновляться обратно в БД
ДатаАдаптер сохраняется в хеш-массиве (чтобы использовать его же затем для вызова метода Update() для данной теблицы)
закрываю соединение.
ДатаСет в БД пытаюсь сохранить следующим образом:
открываю соединение,
Для всех таблиц:
{
получаю изменения, внесенные в Датасет
отыскиваю в хеш-массиве по имени таблицы нужный ДатаАдаптер,
вызываю Update() найденного Адаптера,
}
закрываю соединение.
Проблема: например, при добавлении трех записей в еще пустую таблицу ДатаСета он генерирует для записей следующие ID:
0
1
2
Пытаюсь передать изменения в Аксесс. Передается первая строка, но Аксесс сам для нее сгенерирует ID, равное 1 (а не 0, как до этого сгенерировал ДатаСет). Пытаюсь вернуть ID, сгенерированное Аксессом в Датасет (в обработчике события RowUpdated() запрашивается @@IDENTITY):
private static void OnRowUpdated(object sender, OleDbRowUpdatedEventArgs args)
{
// Include a variable and a command to retrieve the identity value from the Access database.
int newID = 0;
OleDbCommand idCMD = new OleDbCommand("SELECT @@IDENTITY", odcConn);
if (args.StatementType == StatementType.Insert)
{
// Retrieve the identity value and store it in the ID column.
newID = (int) idCMD.ExecuteScalar();
args.Row["ID"] = newID;
}
}
|
но когда программа доходит до строки
args.Row["ID"] = newID;
генерируется исключение, т.к. в ДатаСете уже существует поле ID со значением 1
Собственно вопрос: как с этим бороться?????
Сам подход для работы с БД верно выбран??? (то, что я загружаю в ДатаСет ВСЮ БД??? то, что БД загружается отдельно по табличкам???)
Класс для работы с Датасетом приведен ниже (здесь грузятся не все таблицы из БД, а только четыре).
Для доступа к ДатаСету нужно из программы обратится к полю CDBMan.Instance.dstbData - если ДатаСет уже заполнен, то получаем ссылку на него, если еще не заполнен, то создается новый и заполняется.
Для записи изменений обратно в БД - метод CDBMan.vUpdate()
using System;
using System.Data;
using System.Data.OleDb;
using System.Collections;
using System.Windows.Forms;
namespace TestBed2006
{
/// <summary>
/// Summary description for CDBMan.
/// </summary>
public class CDBMan
{
private static object lockFlag = new object();
private static CDBMan instance;
private static string sconnectionstring =
"Jet OLEDB:Global Partial Bulk Ops=2;" +
"Jet OLEDB:Registry Path=;" +
"Jet OLEDB:Database Locking Mode=1;" +
"Data Source=\"D:\\ArtemK\\Projects\\TestBed\\Data\\TestBed2006.mdb\";" + "Mode=Share Deny None;" +
"Jet OLEDB:Engine Type=5;" +
"Provider=\"Microsoft.Jet.OLEDB.4.0\";" +
"Jet OLEDB:System database=;" +
"Jet OLEDB:SFP=False;" +
"persist security info=False;" +
"Extended Properties=;" +
"Jet OLEDB:Compact Without Replica Repair=False;" +
"Jet OLEDB:Encrypt Database=False;" +
"Jet OLEDB:Create System Database=False;" +
"Jet OLEDB:Don't Copy Locale on Compact=False;" +
"User ID=Admin;Jet OLEDB:Global Bulk Transactions=1";
private static OleDbConnection odcConn = new OleDbConnection(sConString);
private static Hashtable htAdapters = new Hashtable();
public dsTestBed2006 dstbData = new dsTestBed2006();
public static string sConString
{
get
{
return sconnectionstring;
}
set
{
sconnectionstring = value;
vReload();
}
}
public static void vReload()
{
instance = null;
}
private static void vFillTable(string sTblName)
{
OleDbDataAdapter oddaAdapt = new OleDbDataAdapter();
oddaAdapt.SelectCommand = new OleDbCommand("select * from " + sTblName, odcConn);
OleDbCommandBuilder odcbBuilder = new OleDbCommandBuilder(oddaAdapt);
oddaAdapt.Fill(instance.dstbData.Tables[sTblName]);
oddaAdapt.RowUpdated += new OleDbRowUpdatedEventHandler(OnRowUpdated);
htAdapters.Add(sTblName, oddaAdapt);
}
private static void vUpdateTable(string sTblName)
{
DataSet dsTmp = instance.dstbData.GetChanges();
if (dsTmp != null)
{
OleDbDataAdapter oddaAdapt = (OleDbDataAdapter) htAdapters[sTblName];
oddaAdapt.Update(dsTmp.Tables[sTblName]);
}
}
private static void OnRowUpdated(object sender, OleDbRowUpdatedEventArgs args)
{
// Include a variable and a command to retrieve the identity value from the Access database.
int newID = 0;
OleDbCommand idCMD = new OleDbCommand("SELECT @@IDENTITY", odcConn);
if (args.StatementType == StatementType.Insert)
{
// Retrieve the identity value and store it in the ID column.
newID = (int) idCMD.ExecuteScalar();
args.Row["ID"] = newID;
}
}
public static void vUpdate()
{
lock(lockFlag)
{
if(instance != null)
{
odcConn.Open();
vUpdateTable("TesterCategory");
vUpdateTable("TesterExemplar");
vUpdateTable("SysPower");
vUpdateTable("SysAnlComm");
odcConn.Close();
}
}
}
public static CDBMan Instance
{
get
{
lock(lockFlag)
{
if(instance == null)
{
instance = new CDBMan();
odcConn = new OleDbConnection(sConString);
odcConn.Open();
vFillTable("TesterCategory");
vFillTable("TesterExemplar");
vFillTable("SysPower");
vFillTable("SysAnlComm");
odcConn.Close();
}
}
return instance;
}
}
}
}
|