using System; using System.Data; namespace mergeTest { class Class1 { static void Main(string[] args) { // Создается объект DataSet. DataSet ds = new DataSet("myDataSet"); // Создается таблица. DataTable t = new DataTable("Items"); // Столбцы таблицы – это особые объекты. // Имя первого столбца – id, тип значения – System.Int32. DataColumn c1 = new DataColumn("id", Type.GetType("System.Int32")); c1.AutoIncrement=true; // Имя второго столбца – Item, тип значения – System.Int32. DataColumn c2 = new DataColumn("Item", Type.GetType("System.Int32")); // Сборка объекта DataSet: // Добавляются объекты-столбцы... t.Columns.Add(c1); t.Columns.Add(c2); // А вот массив столбцов (здесь он из одного элемента) // для организации первичного ключа (множества первичных ключей). DataColumn[] keyCol= new DataColumn[1]; // И вот, собственно, как в таблице задается множество первичных ключей. keyCol[0]= c1; // Свойству объекта t передается массив, содержащий столбцы, которые // формируемая таблица t будет воспринимать как первичные ключи. t.PrimaryKey=keyCol; // А что с этими ключами будет t делать? А это нас в данный момент // не касается. Очевидно, что методы, которые обеспечивают контроль // над информацией в соответствии со значениями ключей, уже где-то // "зашиты" в классе DataTable. Как и когда они будут выполняться – // не наше дело. Наше дело – указать на столбцы, которые для данной // таблицы будут ключевыми. Что мы и сделали. // Таблица подсоединяется к объекту ds – представителю класса DataSet. ds.Tables.Add(t); DataRow r; // В таблицу, которая уже присоединена к // объекту ds DataSet, добавляется 10 rows. for(int i = 0; i <10;i++) { r=t.NewRow(); r["Item"]= i; t.Rows.Add(r); } // Принять изменения. // Так производится обновление DataSet'а. // Сведения о новых изменениях и добавлениях будут фиксироваться // вплоть до нового обновления. ds.AcceptChanges(); PrintValues(ds, "Original values"); // Изменение значения в первых двух строках. t.Rows[0]["Item"]= 50; t.Rows[1]["Item"]= 111; t.Rows[2]["Item"]= 111; // Добавление еще одной строки. // Судя по всему, значение первого столбца устанавливается автоматически. // Это ключевое поле со значением порядкового номера строки. r=t.NewRow(); r["Item"]=74; t.Rows.Add(r); // Объявляем ссылку для создания временного DataSet. DataSet xSet; // ДЕКЛАРАЦИЯ О НАМЕРЕНИЯХ КОНТРОЛЯ ЗА КОРРЕКТНОСТЬЮ ЗНАЧЕНИЙ СТРОКИ. // Вот так добавляется свойство, содержащее строку для описания // ошибки в значении. Наш DataSet содержит одну строку с описанием. // Это всего лишь указание на то обстоятельство, что МЫ САМИ // обязались осуществлять // некоторую деятельность по проверке чего-либо. Чтобы не забыть, // в чем проблема, // описание возможной ошибки (в свободной форме!) добавляем // в свойства строки, // значения которой требуют проверки. t.Rows[0].RowError= "over 100 (ЙЦУКЕН!)"; t.Rows[1].RowError= "over 100 (Stupid ERROR!)"; t.Rows[2].RowError= "over 100 (Ну и дела!)"; // Но одно дело – декларировать намерения, а другое – осуществлять // контроль. // Проблема проверки корректности значения – наша личная проблема. // Однако о наших намерениях контроля за значениями становится // известно объекту – представителю DataSet! PrintValues(ds, "Modified and New Values"); // Мы вроде бы согласились проводить контроль значений. // Даже декларировали некий принцип проверки. // Однако ничего само собой не происходит. // Так вот, // // ЕСЛИ В ТАБЛИЦУ БЫЛИ ДОБАВЛЕНЫ СТРОКИ ИЛИ ИЗМЕНЕНЫ ЗНАЧЕНИЯ СТРОК // И // МЫ ОБЯЗАЛИСЬ КОНТРОЛИРОВАТЬ ЗНАЧЕНИЯ СТРОК В ТАБЛИЦЕ, // // то самое время организовать эту проверку... // Критерий правильности значений, естественно, наш! // Алгоритмы проверки – тоже НАШИ! // Единственное, чем нам может помочь ADO .NET, – это выделить // подмножество строк таблицы, // которые были добавлены или модифицированы со времени последнего // обновления нашего объекта - представителя DataSet'а, if(ds.HasChanges(DataRowState.Modified | DataRowState.Added)& ds.HasErrors) { // И для этого мы воспользуемся методом, который позволяет обеспечить // выделение подмножества добавленных и // модифицированных строк в новый объект DataSet'а. // Use GetChanges to extract subset. xSet = ds.GetChanges(DataRowState.Modified|DataRowState.Added); PrintValues(xSet, "Subset values"); // Insert code to reconcile errors. In this case, we'll reject changes. // Вот, собственно, код проверки. Все делается своими руками. foreach(DataTable xTable in xSet.Tables) { if (xTable.HasErrors) { foreach(DataRow xRow in xTable.Rows) { // Выделенное подмножество проверяем на наличие // ошибочного значения (для нас все, что больше 100, – // уже ошибка!) Console.Write(xRow["Item"] + " "); if((int)xRow["Item",DataRowVersion.Current ]> 100) { // Находим ошибку в строке, сообщаем о ней, Console.WriteLine("Error! – " + xRow.RowError); // Возвращаем старое значение... xRow.RejectChanges(); // Отменяем значение свойства - уведомителя о возможных // ошибках для данной строки... xRow.ClearErrors(); } else Console.WriteLine("OK."); } } } PrintValues(xSet, "Reconciled subset values"); // Сливаем измененные и откорректированные строки в основной // объект – DataSet // Merge changes back to first DataSet. ds.Merge(xSet); PrintValues(ds, "Merged Values"); } } // А это всего лишь вывод содержимого DataSet'а. private static void PrintValues(DataSet ds, string label) { Console.WriteLine("\n" + label); foreach(DataTable t in ds.Tables) { Console.WriteLine("TableName: " + t.TableName); foreach(DataRow r in t.Rows) { foreach(DataColumn c in t.Columns) { Console.Write("\t " + r[c] ); } Console.WriteLine(); } } } } }