Własna kontrolka SSIS – część 10 – DBCommand i parametry

Jedną z zalet używania mało wykorzystywanych rozwiązań jest fakt, że nie wszystko co można zrobić dalej da się znaleźć w Google. Albo da się znaleźć, ale proponowane rozwiązanie nie działa (albo dotyczy wywołania procedury zamiast polecenia SQL). Czyli wracamy do starej dobrej metody prób i błędów. Ot – takie DBCommand użyte jako warstwa abstrakcji ponieważ użyłem OLE DB jako jednego z managerów połączeń może skutkować kilkoma godzinami siedzenia nad jednym krótkim kawałkiem kodu.

Problem jaki się pojawił w przypadku kontrolki to zapis informacji o pobranych plikach w bazie. Samo wywołanie komendy to nic trudnego. Dla uproszczenia przyjmijmy zwykłe polecenie INSERT:

DbCommand cmd = this.connection.CreateCommand();
cmd.CommandText = @"
    INSERT INTO dbo.DownloadedFiles (RemoteFilePath, RemoteDirectoryName, LocalFileName, AuditKey, FileStatusId)
    VALUES ('A', 'B', 'C', 1, 1)
";

cmd.ExecuteNonQuery();

I wszystko gra. Ale jeśli chcemy użyć parametrów, to już jest nieco trudniej.

Przeglądając różne samouczki, posty na blogach czy odpowiedzi np. na StackOverflow dowiadujemy się, że należy parametryzować nasze komendy i używać metody AddWithValue(), żeby te parametry odpowiednio ustawić. Niestety interfejs IDbCommand nie zna AddWithValue(). Parametry definiujemy przez CreateParameter() i dodajemy przez Add(parametr):

var paramRemoteFilePath = cmd.CreateParameter();
paramRemoteFilePath.ParameterName = "RemoteFilePath";
paramRemoteFilePath.Value = "abc";
// opcjonalnie
paramRemoteFilePath.DbType = DbType.String;

cmd.Parameters.Add(paramRemoteFilePath);

Dokumentacja też nie podpowiada za wiele. Dowiadujemy się, że w zależności od data providera używamy albo znaków zapytania – ?, albo parametrów małpy – @, albo dwukropka – :. Tyle, że żaden z nich nie chciał zadziałać dla mojego wywołania DBCommand. Znak zapytania czy dwukropek to nieprawidłowe znaki polecenia. Znak małpy – trzeba zadeklarować zmienną. Z grzebania po forach social.msdn.microsoft.com wyszła propozycja użycia znaku dolara – $ przed nazwą zmiennej, ale też nic (traktuje jako pseudokoumnę).

Kiedy już kombinowałem, żeby przepisać INSERT na procedurę (skoro parametry ze znakiem @ rozumie) to zrobiłem jeszcze kilka testów z każdą kombinacją znaków zapytań i małp jaka mi przyszła do głowy. I wreszcie ruszyło.

Co się ostatecznie okazało. Testowane polecenie miało mieć docelowo trzy parametry. Zostały one dodane od razu w ramach sprawdzania podejścia z użyciem @ przy nazwie parametru. Potem polecenie SQL było edytowane na różne sposoby, ale tylko z jednym parametrem podmienianym na różne sposoby – pozostałe dwa parametry w poleceniu były jako zwykłe ciągi znaków. Czyli zaczynałem od czegoś takiego:

DbCommand cmd = this.connection.CreateCommand();
cmd.CommandType = CommandType.Text;
cmd.CommandText = @"
    INSERT INTO dbo.DownloadedFiles (RemoteFilePath, RemoteDirectoryName, LocalFileName, AuditKey, FileStatusId)
    VALUES (?, 'X', 'Y', 1, 1);";

var paramRemoteFilePath = cmd.CreateParameter();
paramRemoteFilePath.ParameterName = "RemoteFilePath";
paramRemoteFilePath.Value = "abc";
paramRemoteFilePath.DbType = DbType.String;

var paramRemoteDirectoryName = cmd.CreateParameter();
paramRemoteDirectoryName.ParameterName = "RemoteDirectoryName";
paramRemoteDirectoryName.Value = "/abc/def";

var paramLocalFileName = cmd.CreateParameter();
paramLocalFileName.ParameterName = "LocalFileName";
paramLocalFileName.Value = "_abc_def";

cmd.Parameters.Add(paramRemoteFilePath);
cmd.Parameters.Add(paramRemoteDirectoryName);
cmd.Parameters.Add(paramLocalFileName);

cmd.ExecuteNonQuery();

W tym przypadku pojawiał się komunikat Parametr polecenia[1] ” jest nieprawidłowy. Parametr polecenia[2] ” jest nieprawidłowy. Oczywiście kto zwraca uwagę na informacje dodatkowe – np. że wymieniono parametr dwa razy? Kiedy zakomentowałem polecenia:

//cmd.Parameters.Add(paramRemoteDirectoryName);
//cmd.Parameters.Add(paramLocalFileName);

zadziałało bez problemu! Czyli głównym problemem był nadmiar parametrów.

Skończyło się na:

DbCommand cmd = this.connection.CreateCommand();
cmd.CommandType = CommandType.Text;
cmd.CommandText = @"
    INSERT INTO dbo.DownloadedFiles (RemoteFilePath, RemoteDirectoryName, LocalFileName, AuditKey, FileStatusId)
    VALUES (?, ?, ?, 1, 1);";

var paramRemoteFilePath = cmd.CreateParameter();
paramRemoteFilePath.ParameterName = "RemoteFilePath";
paramRemoteFilePath.Value = "abc";
paramRemoteFilePath.DbType = DbType.String;

var paramRemoteDirectoryName = cmd.CreateParameter();
paramRemoteDirectoryName.ParameterName = "RemoteDirectoryName";
paramRemoteDirectoryName.Value = "/abc/def";

var paramLocalFileName = cmd.CreateParameter();
paramLocalFileName.ParameterName = "LocalFileName";
paramLocalFileName.Value = "_abc_def";

cmd.Parameters.Add(paramRemoteFilePath);
cmd.Parameters.Add(paramRemoteDirectoryName);
cmd.Parameters.Add(paramLocalFileName);

cmd.ExecuteNonQuery();

Rzecz jasna nie jest to kod skończony, ale punkt wyjścia do dalszych modyfikacji. Wniosek na przyszłość – używaj na raz tylko tylu parametrów ile potrzebujesz. I do poprawnego działania DBCommand z OLE DB jednak znak zapytania działa bardzo dobrze.

Reklamy

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