To pytanie pojawia się czasami, ale nie widziałem satysfakcjonującej odpowiedzi.
Typowy wzorzec to (row to DataRow ):
if (row["value"] != DBNull.Value)
{
someObject.Member = row["value"];
}
Moje pierwsze pytanie brzmi, co jest bardziej wydajne (odwróciłem warunek):
row["value"] == DBNull.Value; // Or
row["value"] is DBNull; // Or
row["value"].GetType() == typeof(DBNull) // Or... any suggestions?
Oznacza to, że .GetType () powinno być szybsze, ale może kompilator zna kilka sztuczek, których ja nie mam?
Drugie pytanie, czy warto buforować wartość wiersza [„wartość”], czy i tak kompilator optymalizuje indeksator?
Na przykład:
object valueHolder;
if (DBNull.Value == (valueHolder = row["value"])) {}
Uwagi:
- istnieje wiersz [„wartość”].
- Nie znam indeksu kolumny (stąd wyszukanie nazwy kolumny).
- Pytam konkretnie o sprawdzenie DBNull, a następnie przypisanie (nie o przedwczesną optymalizację itp.).
Porównałem kilka scenariuszy (czas w sekundach, 10 000 000 prób):
row["value"] == DBNull.Value: 00:00:01.5478995
row["value"] is DBNull: 00:00:01.6306578
row["value"].GetType() == typeof(DBNull): 00:00:02.0138757
Object.ReferenceEquals ma taką samą wydajność jak „==”
Najciekawszy wynik? Jeśli nie dopasujesz nazwy kolumny według wielkości liter (na przykład „Wartość” zamiast „wartość”, trwa to około dziesięć razy dłużej (w przypadku ciągu):
row["Value"] == DBNull.Value: 00:00:12.2792374
Morał z tej historii wydaje się być taki, że jeśli nie możesz wyszukać kolumny według jej indeksu, upewnij się, że nazwa kolumny, którą przekazujesz indeksatorowi, dokładnie odpowiada nazwie kolumny DataColumn.
Buforowanie wartości również wydaje się być prawie dwa razy szybsze:
No Caching: 00:00:03.0996622
With Caching: 00:00:01.5659920
Wydaje się więc, że najbardziej wydajną metodą jest:
object temp;
string variable;
if (DBNull.Value != (temp = row["value"]))
{
variable = temp.ToString();
}
IDataRecord
rozszerzenia.Odpowiedzi:
Muszę czegoś przegapić. Czy nie sprawdza
DBNull
dokładnie, coDataRow.IsNull
robi ta metoda?Używałem następujących dwóch metod rozszerzających:
Stosowanie:
Jeśli nie chcesz
Nullable<T>
zwracać wartości dlaGetValue<T>
, możesz łatwo zwrócićdefault(T)
lub inną opcję zamiast.Z drugiej strony, oto alternatywa VB.NET dla sugestii Stevo3000:
źródło
row.IsNull(columnName)
, czytasz to już raz i czytasz ponownie. Nie mówię, że to zrobi różnicę, ale teoretycznie może być mniej wydajne ..System.Data.DataSetExtensions.DataRowExtensions.Field<T>(this System.Data.DataRow, string)
zasadniczo nie robi tego samego, co pierwsza metoda?Powinieneś użyć metody:
Biorąc pod uwagę, że jest wbudowany w Framework, spodziewałbym się, że będzie to najbardziej wydajne.
Proponuję coś w rodzaju:
I tak, kompilator powinien to dla Ciebie buforować.
źródło
Kompilator nie zoptymalizuje indeksatora (tj. Jeśli użyjesz row ["value"] dwa razy), więc tak, jest to nieco szybsze:
a następnie dwukrotnie użyj wartości; używanie .GetType () stwarza ryzyko problemów, jeśli jest null ...
DBNull.Value
jest w rzeczywistości singletonem, więc aby dodać czwartą opcję - być może możesz użyć ReferenceEquals - ale w rzeczywistości myślę, że martwisz się tutaj zbytnio ... Nie sądzę, że prędkość różni się między "is", "== "itp. będzie przyczyną wszelkich napotkanych problemów z wydajnością. Profiluj cały kod i skup się na czymś, co ma znaczenie ... to nie będzie to.źródło
Użyłbym następującego kodu w C # ( VB.NET nie jest tak prosty).
Kod przypisuje wartość, jeśli nie jest ona pusta / DBNull, w przeciwnym razie przypisuje wartość domyślną, która może być ustawiona na wartość LHS, umożliwiając kompilatorowi zignorowanie przypisania.
źródło
oSomeObject.IntMember = If(TryCast(oRow("Value), Integer?), iDefault)
.TryCast
nie zapewnia takiej samej wygodnej funkcjonalności, jakas
operator C # dlaNullable(Of T)
typów. Najbliższym sposobem, jaki przychodzi mi do głowy, aby to naśladować, jest napisanie własnej funkcji, tak jak zasugerowałem w mojej odpowiedzi.Wydaje mi się, że tylko kilka podejść tutaj nie naraża perspektywy OP jako największego zmartwienia (Marc Gravell, Stevo3000, Richard Szalay, Neil, Darren Koppand), a większość jest niepotrzebnie skomplikowana. Będąc w pełni świadomym, że jest to bezużyteczna mikro-optymalizacja, powiem, że w zasadzie powinieneś zastosować następujące:
1) Nie czytaj wartości z DataReader / DataRow dwa razy - więc albo buforuj ją przed sprawdzeniami null i rzutami / konwersjami, albo jeszcze lepiej przekaż swój
record[X]
obiekt bezpośrednio do niestandardowej metody rozszerzenia z odpowiednim podpisem.2) Aby zastosować się do powyższego, nie używaj wbudowanej
IsDBNull
funkcji w swoim DataReader / DataRow, ponieważ wywołuje torecord[X]
wewnętrznie, więc w efekcie zrobisz to dwukrotnie.3) Z reguły porównanie typów będzie zawsze wolniejsze niż porównanie wartości. Po prostu zrób
record[X] == DBNull.Value
lepiej.4) Bezpośrednie rzucanie będzie szybsze niż wywołanie
Convert
klasy do konwersji, chociaż obawiam się, że ta ostatnia będzie słabsza.5) Wreszcie, dostęp do rekordu według indeksu zamiast nazwy kolumny będzie ponownie szybszy.
Czuję, że podejście Szalaya, Neila i Darrena Koppanda będzie lepsze. Szczególnie podoba mi się podejście metody rozszerzania Darrena Koppanda, które obejmuje
IDataRecord
(choć chciałbym zawęzić zakresIDataReader
) i nazwę indeksu / kolumny.Uważaj, aby to nazwać:
i nie
w przypadku potrzeby rozróżnienia między
0
iDBNull
. Na przykład, jeśli masz wartości null w polach wyliczenia, w przeciwnym razie istniejedefault(MyEnum)
ryzyko zwrócenia pierwszej wartości wyliczenia. Więc lepiej zadzwońrecord.GetColumnValue<MyEnum?>("Field")
.Ponieważ czytasz z
DataRow
, stworzyłbym metodę rozszerzenia dla obuDataRow
iIDataReader
przez DRYing wspólny kod.Więc teraz nazwij to tak:
Wierzę, że to, jak powinno być w ramach (zamiast
record.GetInt32
,record.GetString
etc metody) w pierwszej kolejności - bez wyjątków run-time i daje nam elastyczność wartości null uchwytem.Z mojego doświadczenia wynika, że miałem mniej szczęścia z jedną ogólną metodą odczytu z bazy danych. I zawsze miał zwyczaj obsługi różnych typów, więc musiałem napisać własny
GetInt
,GetEnum
,GetGuid
, itd metod w dłuższej perspektywie. Co by było, gdybyś chciał przyciąć białe spacje podczas domyślnego odczytywania ciągu z db lub traktowaćDBNull
jako pusty ciąg? Lub jeśli Twój dziesiętny powinien zostać obcięty ze wszystkich końcowych zer. Najwięcej problemów miałem zGuid
typem, w którym różne sterowniki łączników zachowywały się inaczej niż wtedy, gdy bazowe bazy danych mogą przechowywać je jako ciąg lub plik binarny. Mam takie przeciążenie:Przy podejściu Stevo3000 uważam, że wywołanie jest trochę brzydkie i nużące i trudniej będzie zrobić z niego ogólną funkcję.
źródło
Istnieje kłopotliwy przypadek, w którym obiekt może być łańcuchem. Poniższy kod metody rozszerzenia obsługuje wszystkie przypadki. Oto, jak byś tego użył:
źródło
Osobiście preferuję tę składnię, która wykorzystuje jawną metodę IsDbNull ujawnioną przez
IDataRecord
i buforuje indeks kolumny, aby uniknąć podwójnego wyszukiwania ciągów.Rozszerzony dla czytelności wygląda mniej więcej tak:
Przepisano tak, aby zmieścił się w pojedynczym wierszu dla zwartości w kodzie DAL - zwróć uwagę, że w tym przykładzie przypisujemy
int bar = -1
ifrow["Bar"]
jest null.Przypisanie w wierszu może być mylące, jeśli nie wiesz, że tam jest, ale utrzymuje całą operację w jednym wierszu, co moim zdaniem zwiększa czytelność, gdy wypełniasz właściwości z wielu kolumn w jednym bloku kodu.
źródło
Nie żebym to zrobił, ale możesz obejść wywołanie podwójnego indeksatora i nadal utrzymywać kod w czystości, używając metody statycznej / rozszerzającej.
To znaczy.
Następnie:
Ma również tę zaletę, że utrzymuje logikę sprawdzania wartości zerowej w jednym miejscu. Wadą jest oczywiście to, że jest to dodatkowe wywołanie metody.
Tylko myśl.
źródło
W miarę możliwości staram się unikać tego sprawdzania.
Oczywiście nie trzeba tego robić w przypadku kolumn, które nie mogą się utrzymać
null
.Jeśli przechowujesz w typie wartości Nullable (
int?
itp.), Możesz po prostu przekonwertować za pomocąas int?
.Jeśli nie potrzebujesz rozróżniać między
string.Empty
inull
, możesz po prostu wywołać.ToString()
, ponieważ DBNull zwrócistring.Empty
.źródło
Zawsze używam:
Okazało się, że jest krótkie i wyczerpujące.
źródło
Oto jak radzę sobie z czytaniem z DataRows
Przykład użycia:
Props to Monsters Got My .Net for ChageTypeTo code.
źródło
Zrobiłem coś podobnego z metodami rozszerzającymi. Oto mój kod:
Aby go użyć, zrobiłbyś coś takiego
źródło
jeśli w DataRow wiersz ["nazwa pola"] isDbNull zamień go na 0, w przeciwnym razie uzyskaj wartość dziesiętną:
źródło
używać w ten sposób
źródło
Mam IsDBNull w programie, który czyta dużo danych z bazy danych. Z IsDBNull ładuje dane w około 20 sekund. Bez IsDBNull około 1 sekundy.
Więc myślę, że lepiej jest użyć:
źródło