wtorek, 6 maja 2008

Usprawnienia pracy z prostymi providerami ADO.NET

Ostatnio musiałem przenieść dane z nowobudowanego systemu do pewnego dość starego systemu, który korzysta z serwera PostgreSQL. Z tej racji, że system ów jest dość mało znormalizowany podstawowa tabela zawiera mnóstwo kolumn (brrr np. 15 kolumn z poszczególnymi pozycjami planu zajęć). Trochę trwało przygotowanie danych w takim układzie w postaci DataTable, potem pozostało mi tylko napisanie kodu obsługującego zapis danych do bazy postgresowej. W tym celu użyłem darmowego sterownika Npgsql, który jednak ma małe wsparcie do designera, co nie przeszkadza zbyt bardzo przy pisaniu poleceń sql dla znormalizowanych tabel. Jednak dla tabeli z 76 kolumnami oznaczało to perspektywę wklepywania ogromnego tasiemca z treścią sql, a potem jeszcze dłuższego z definiowaniem parametrów polecenia.

Pierwszy problem rozwiązałem przenosząc treść poleceń sql (a właściwie tylko Insert, bo inne nie były mi potrzebne), do osobnego pliku tekstowego znajdującego się w zasobach. Z drugim problemem poradziłem sobie przyjmując założenie, że nazwy parametrów używanych w sql-u będą takie same jak nazwy kolumn w źródłowym obiekcie DataTable, a typ parametru będzie wyznaczany przez typ kolumny z tegoż DataTable. Przy przyjęciu tych założeń wystarczyło w pętli przejść po wszystkich kolumnach tabeli i utworzyć odpowiednie parametry polecenia sql. Kod realizujący ten pomysł umieściłem w następującej klasie:

using System.Collections.Generic;
using System.Data;
using Npgsql;
using NpgsqlTypes;

namespace Utils
{
internal class NpgsqlTableMapping
{
private readonly Dictionary<System.Type, NpgsqlDbType> npgsqlConversions;

public NpgsqlTableMapping()
{
npgsqlConversions = new Dictionary<System.Type, NpgsqlDbType>();

npgsqlConversions.Add(typeof(string), NpgsqlDbType.Varchar);
npgsqlConversions.Add(typeof(int), NpgsqlDbType.Integer);
npgsqlConversions.Add(typeof(short), NpgsqlDbType.Smallint);
npgsqlConversions.Add(typeof(byte), NpgsqlDbType.Smallint);
npgsqlConversions.Add(typeof(decimal), NpgsqlDbType.Numeric);
npgsqlConversions.Add(typeof(bool), NpgsqlDbType.Bit);
}

public void AddMappings(NpgsqlCommand command, DataTable table)
{
foreach (DataColumn column in table.Columns)
{
string parameterName = column.ColumnName;
NpgsqlDbType parameterType = npgsqlConversions[
column.DataType];

NpgsqlParameter parameter = command.Parameters.Add(
parameterName, parameterType);
parameter.SourceColumn = column.ColumnName;
}
}
}
}



Jak widać nie ma tutaj żadnych czarów, a tylko prosty słownik typów .netowych z odpowiadającymi im typami Npgsql (nie wszystkimi, a tylko tymi, które były mi potrzebne. Teraz jednak kod obiektu dal zapisujący dane do bazy postgresowej jest dużo krótszy:



using System.Data;
using Npgsql;
using NpgsqlTypes;


using Utils;

namespace ODDI.VirtualDictionaryDAL
{
public class LearningOffersDAL
{
private NpgsqlDataAdapter adapter;
private NpgsqlConnection connection;

public LearningOffersDAL()
{
InitPostgreConnection();
}

private void InitPostgreConnection()
{
string connectionString = System.Configuration.ConfigurationManager.
ConnectionStrings["PostgreServer"].ConnectionString;

this.connection = new NpgsqlConnection(connectionString);
}

private bool initedOffersAdapter = false;
private void InitOffersAdapter(DataTable table)
{
if(!initedOffersAdapter)
{
string sql = Sgh.ODDI.VirtualDictionaryDAL.Properties.
Resources.InsertLearningOffer;

NpgsqlCommand insertCommand = new NpgsqlCommand(sql, connection);
insertCommand.Parameters.Add("YearSemester", NpgsqlDbType.Integer);

adapter = new NpgsqlDataAdapter();
adapter.InsertCommand = insertCommand;

NpgsqlTableMapping mappings = new NpgsqlTableMapping();
mappings.AddMappings(this.adapter.InsertCommand, table);

initedOffersAdapter = true;
}
}

public void Update(DataTable table, int yearSemester)
{
InitOffersAdapter(table);

this.adapter.InsertCommand.Parameters["YearSemester"].
Value = yearSemester;

this.adapter.Update(table);
}
}
}



Użycie obiektu NpgsqlTableMapping nie przeszkadza w dodaniu dodatkowych parametrów (tutaj YearSemester). Wadą jest natomiast to, że polecenie może być przygotowane dopiero przy pierwszym update, kiedy znana jest definicja źródłowego DataTable.

0 komentarze: