Śledzenie i kontrola zmian danych w obiekcie DataTable

2012-06-03

W dzisiejszym wpisie zajmę się tematem śledzenia zmian i kontroli danych w obiekcie DataTable. Jest to możliwe dzięki temu, że wiersze tabeli będące obiektami typu DataRow przechowują informacje o swoich wersjach. Obiekty DataRow posiadają także szereg metod związanych z modyfikacją danych, a co za tym idzie ze zmianą ich wersji. Do pobrania określonej wersji obiektu DataRow służy enum DataRowVersion posiadający następujące elementy:

  • Original – oryginalna wersja danych (po ostatnim wykonaniu metody AcceptChanges)
  • Current – aktualna wersja danych
  • Proposed – proponowana wersja danych
  • Default – domyślna wersja danych

Do sprawdzenia czy dana wersja istnieje służy właściwość DataRow.HasVersion przyjmująca jako parametr jeden z elementów powyższego enum-a, w wyniku zwracając wartość bool. W celu pobrania wartości kolumny z określonej wersji rekordu musimy po nazwie lub identyfikatorze kolumny podać wersję jaka nas interesuje.

Zmiany poszczególnych wersji wiersza tabeli mają miejsce podczas wywoływania następujących metod obiektu DataRow:

  • BeginEdit() – rozpoczęcie edycji, wersja Current zawiera aktualną wersję danych, Proposed zawiera zmodyfikowane dane
  • EndEdit() – zakończenie edycji, wersja Proposed staje się wersją Current
  • CancelEdit() – anulowanie edycji, wersja Proposed jest usuwana
  • AcceptChanges() – zatwierdzenie wszystkich zmian od ostatniego wywołania AcceptChanges, wersja Original przyjmuje wartości z wersji Current
  • RejectChanges() – wycofanie wszystkich zmian od ostatniego wywołania AcceptChanges (wycofywane są także zmiany mimo wywołania EndEdit)

Metody AcceptChanges i RejectChanges dostępne są także na obiekcie DataTable, działają one analogicznie do powyższych ale swoim zasięgiem obejmują wszystkie wiersze tabeli.

W jakim celu należy używać powyższych metod skoro modyfikacja danych w DataTable możliwa jest bez nich? Załóżmy, że zmieniamy dane w wybranym wierszu obiektu DataTable z przypisanym zdarzeniem RowChanging, w którym kontrolujemy poprawność wprowadzanych danych. Jeżeli nie użyjemy tych metod zdarzenie będzie wywoływane przy zmianie wartości w każdej kolumnie, co negatywnie odbije się na wydajności. Najlepiej gdyby walidacja wiersza uruchomiana została dopiero w momencie zakończenia modyfikacji wszystkich kolumn. Dlatego właśnie warto stosować opisane metody. Po wywołaniu DataRow.BeginEdit wstrzymywane jest wyzwalanie zdarzeń RowChanging i RowChanged do momentu wywołania DataRow.EndEdit. We wspomnianych zdarzeniach warto na początku sprawdzić stan wiersza (e.Row.RowState) lub rodzaj akcji (e.Action) ponieważ są one wywoływane zarówno podczas EndEdit jak i AcceptChanges oraz RejectChanges. Action przyjmuje wartość Add, Change lub Delete w przypadku EndEdit, Commit w przypadku AcceptChanges oraz Rollback w przypadku RejectChanges.

Poniższy kod pokazuje przykładowe zastosowanie omawianych mechanizmów. Tworzony jest obiekt DataTable, dodawane są do niego dwa rekordy, następnie pierwszy z nich jest modyfikowany. Ponadto w zdarzeniu RowChanging sprawdzane jest czy podczas modyfikacji danych kolumna Id nie przyjmuje wartości mniejszej niż miała wcześniej oraz czy kolumna Name nie jest pusta:

private void DataTableExample()
{
    DataTable dataTable = new DataTable();
    dataTable.Columns.Add("Id", Type.GetType("System.Int32"));
    dataTable.Columns.Add("Name", Type.GetType("System.String"));

    dataTable.Rows.Add(1, "Name_1");
    dataTable.Rows.Add(2, "Name_2");
    dataTable.AcceptChanges();

    dataTable.RowChanging += new DataRowChangeEventHandler(dataTable_RowChanging);

    DataRow dataRow = dataTable.Rows[0];

    dataRow.BeginEdit();
    /*
    dataRow["Id", DataRowVersion.Original]: 1
    dataRow["Id", DataRowVersion.Current]: 1
    */
    dataRow["Id"] = 3;
    dataRow["Name"] = "Name_3";
    /*
    dataRow["Id", DataRowVersion.Original]: 1
    dataRow["Id", DataRowVersion.Current]: 1
    dataRow["Id", DataRowVersion.Proposed]: 3
    */
    try
    {
        dataRow.EndEdit();
        /*
        dataRow["Id", DataRowVersion.Original]: 1
        dataRow["Id", DataRowVersion.Current]: 3
        */
    }
    catch (Exception ex)
    {
        dataRow.CancelEdit();
    }
    if (dataRow.RowState == DataRowState.Modified)
        dataRow.AcceptChanges();
    /*
    dataRow["Id", DataRowVersion.Original]: 3
    dataRow["Id", DataRowVersion.Current]: 3
    */
}

private void dataTable_RowChanging(object sender, DataRowChangeEventArgs e)
{
    if (e.Action == DataRowAction.Change)
    {
        int currentId = (int)e.Row["Id", DataRowVersion.Current];
        int proposedId = currentId;
        if (e.Row.HasVersion(DataRowVersion.Proposed))
            proposedId = (int)e.Row["Id", DataRowVersion.Proposed];
        //W tym miejscu wersja Proposed jest domyślna więc można ją pobrać bezpośrednio, powyższa konstrukcja użyta została w celach prezentacji
        string proposedName = e.Row["Name"].ToString();

        if ((proposedId < currentId) || string.IsNullOrWhiteSpace(proposedName))
            throw new Exception("Nieprawidłowe dane!");
    }
}

Więcej informacji na ten temat można znaleźć na MSDN: DataRowDataRowVersion

Reklamy

Posted on 2012-06-03, in .NET/C# and tagged , , , . Bookmark the permalink. Dodaj komentarz.

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Wyloguj / Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Wyloguj / Zmień )

Zdjęcie na Facebooku

Komentujesz korzystając z konta Facebook. Wyloguj / Zmień )

Zdjęcie na Google+

Komentujesz korzystając z konta Google+. Wyloguj / Zmień )

Connecting to %s

%d blogerów lubi to: