public static class DataRecordExtensions
{
public static bool HasColumn(this IDataRecord dr, string columnName)
{
for (int i=0; i < dr.FieldCount; i++)
{
if (dr.GetName(i).Equals(columnName, StringComparison.InvariantCultureIgnoreCase))
return true;
}
return false;
}
}
Używanie Exception
s do logiki sterowania, jak w niektórych innych odpowiedziach, jest uważane za złą praktykę i wiąże się z kosztami wydajności. Wysyła również fałszywe alarmy do profilera zgłoszonych # wyjątków i niech Bóg pomoże każdemu, kto ustawi swojego debuggera na złamanie zgłoszonych wyjątków.
GetSchemaTable () to także kolejna sugestia w wielu odpowiedziach. Nie byłby to preferowany sposób sprawdzania istnienia pola, ponieważ nie jest on implementowany we wszystkich wersjach (jest abstrakcyjny i zgłasza NotSupportedException w niektórych wersjach dotnetcore). GetSchemaTable ma również zbyt dużą wydajność, ponieważ jest to dość ciężka funkcja, jeśli sprawdzisz źródło .
Pętlowanie przez pola może mieć niewielki wpływ na wydajność, jeśli często go używasz i możesz rozważyć buforowanie wyników.
O wiele lepiej jest użyć tej funkcji boolowskiej:Jedno połączenie - bez wyjątków. Może powodować wewnętrzne wyjątki, ale nie sądzę.UWAGA: W komentarzach poniżej zorientowaliśmy się ... Właściwy kod to tak naprawdę:
źródło
Myślę, że najlepszym rozwiązaniem jest wywołanie GetOrdinal („columnName”) na swoim DataReaderze z góry i wyłapanie IndexOutOfRangeException na wypadek, gdyby kolumna nie była obecna.
W rzeczywistości stwórzmy metodę rozszerzenia:
Edytować
Ok, ten post zaczyna ostatnio zbierać kilka głosów negatywnych i nie mogę go usunąć, ponieważ jest to zaakceptowana odpowiedź, więc zaktualizuję go i (mam nadzieję) spróbuję uzasadnić użycie obsługi wyjątków jako kontrola przepływu.
Innym sposobem osiągnięcia tego, jak napisał Chad Grant , jest przejście przez każde pole w czytniku danych i wykonanie rozróżniania wielkości liter dla szukanej nazwy pola. To zadziała naprawdę dobrze i prawdę mówiąc prawdopodobnie będzie działało lepiej niż moja metoda powyżej. Z pewnością nigdy nie użyłbym powyższej metody w pętli, w której występowanie stanowiło problem.
Mogę wymyślić jedną sytuację, w której metoda try / GetOrdinal / catch będzie działać tam, gdzie pętla nie działa. Jest to jednak obecnie całkowicie hipotetyczna sytuacja, więc jest to bardzo słabe uzasadnienie. Niezależnie od tego, znoś mnie i zobacz, co myślisz.
Wyobraź sobie bazę danych, która pozwoliła na „alias” kolumn w tabeli. Wyobraź sobie, że mógłbym zdefiniować tabelę z kolumną o nazwie „Nazwa pracownika”, ale także nadać jej alias „Nazwa Emp”, a wybranie jednej z nazw zwróciłoby dane w tej kolumnie. Do tej pory ze mną?
Wyobraź sobie teraz, że istnieje dostawca ADO.NET dla tej bazy danych i zakodowali dla niej implementację IDataReader, która uwzględnia aliasy kolumn.
Teraz
dr.GetName(i)
(użyte w odpowiedzi Chada) może zwrócić tylko jeden ciąg, więc musi zwrócić tylko jeden z „aliasów” w kolumnie. JednakGetOrdinal("EmpName")
przydałby wewnętrzną realizację polach tego dostawcy w celu sprawdzenia każdej kolumny alias dla nazwy, której szukasz.W tej hipotetycznej sytuacji „kolumny z aliasem” metoda try / GetOrdinal / catch byłaby jedynym sposobem, aby upewnić się, że sprawdzane są wszystkie odmiany nazwy kolumny w zestawie wyników.
Słaby? Pewnie. Ale warto pomyśleć. Szczerze mówiąc wolałbym raczej „oficjalną” metodę HasColumn na IDataRecord.
źródło
W jednym wierszu użyj tego po pobraniu DataReadera:
Następnie,
Edytować
Znacznie bardziej wydajna jedna linijka, która nie wymaga ładowania schematu:
źródło
Oto działająca próbka pomysłu Jasmina:
źródło
to działa dla mnie:
źródło
Poniższe jest proste i działało dla mnie:
źródło
Jeśli przeczytasz pytanie, Michael zapytał o DataReader, a nie ludzi DataRecord. Zrób swoje przedmioty we właściwy sposób.
Używać
r.GetSchemaTable().Columns.Contains(field)
na DataRecord działa, ale zwraca kolumny BS (patrz zrzut ekranu poniżej).Aby sprawdzić, czy kolumna danych istnieje ORAZ zawiera dane w czytniku danych, użyj następujących rozszerzeń:
Stosowanie:
Wywołanie
r.GetSchemaTable().Columns
w DataReader zwraca kolumny BS:źródło
IDataReader
implementujeIDataRecord
. Są to różne interfejsy tego samego obiektu - podobnie jakICollection<T>
iIEnumerable<T>
są różne interfejsyList<T>
.IDataReader
umożliwia przejście do następnego rekordu, jednocześnieIDataRecord
umożliwiając odczyt z bieżącego rekordu. Wszystkie metody użyte w tej odpowiedzi pochodzą zIDataRecord
interfejsu. Zobacz stackoverflow.com/a/1357743/221708, aby uzyskać wyjaśnienie, dlaczego warto zadeklarować parametr jakoIDataRecord
preferowany.r.GetSchemaTable().Columns
odpowiedź na to pytanie jest absolutnie zła.Napisałem dla użytkowników Visual Basic:
Myślę, że jest to bardziej wydajne, a użycie jest następujące:
źródło
Oto jedna liniowa wersja linq akceptowanej odpowiedzi:
źródło
Oto rozwiązanie od Jasmine w jednym wierszu ... (jeszcze jedno, proste!):
źródło
źródło
TLDR:
Wiele odpowiedzi z twierdzeniami na temat wydajności i złych praktyk, więc wyjaśniam to tutaj.
Trasa wyjątku jest szybsza dla większej liczby zwracanych kolumn, trasa pętli jest szybsza dla mniejszej liczby kolumn, a punkt podziału wynosi około 11 kolumn. Przewiń w dół, aby zobaczyć wykres i kod testowy.
Pełna odpowiedź:
Kod niektórych najważniejszych odpowiedzi działa, ale tutaj jest debata na temat „lepszej” odpowiedzi opartej na akceptacji obsługi wyjątków w logice i związanej z nią wydajności.
Aby to wyjaśnić, nie wierzę, że istnieje wiele wskazówek dotyczących wyjątków ŁOWIENIE. Microsoft ma pewne wskazówki dotyczące wyjątków WYPROWADZANIE. Tam stwierdzają:
Pierwsza uwaga to łagodność „jeśli to możliwe”. Co ważniejsze, opis podaje następujący kontekst:
Oznacza to, że jeśli piszesz interfejs API, który może być używany przez kogoś innego, daj mu możliwość poruszania się po wyjątku bez próbowania / chwytania. Na przykład podaj TryParse ze swoją metodą analizy wyjątków Parse. Nigdzie nie mówi to jednak, że nie powinieneś wychwytywać wyjątku.
Ponadto, jak zauważa inny użytkownik, połowy zawsze pozwalały na filtrowanie według typu, a nieco później na dalsze filtrowanie poprzez klauzulę when . Wydaje się to stratą funkcji językowych, jeśli nie powinniśmy ich używać.
Można powiedzieć, że istnieje NIEKTÓRY koszt rzuconego wyjątku, który może MNIE wpłynąć na wydajność w ciężkiej pętli. Można jednak powiedzieć, że koszt wyjątku będzie znikomy w „połączonej aplikacji”. Rzeczywisty koszt zbadano ponad dziesięć lat temu: https://stackoverflow.com/a/891230/852208 Innymi słowy, koszt połączenia i zapytania do bazy danych może być niższy niż w przypadku zgłoszonego wyjątku.
Poza tym chciałem ustalić, która metoda naprawdę jest szybsza. Zgodnie z oczekiwaniami nie ma konkretnej odpowiedzi.
Każdy kod, który zapętla się nad kolumnami, staje się wolniejszy w miarę istnienia liczby kolumn. Można również powiedzieć, że każdy kod, który opiera się na wyjątkach, zwolni w zależności od częstotliwości, w której zapytanie nie zostanie znalezione.
Biorąc odpowiedzi zarówno Chada Granta, jak i Matta Hamiltona, zastosowałem obie metody z maksymalnie 20 kolumnami i do 50% poziomu błędu (OP wskazał, że używa tego testu między różnymi procesami, więc założyłem, że tylko dwa) .
Oto wyniki przedstawione za pomocą LinqPad:
Zygzaki tutaj są wskaźnikami błędów (nie znaleziono kolumny) w obrębie każdej liczby kolumn.
W porównaniu z węższymi zestawami wyników, zapętlenie jest dobrym wyborem. Jednak metoda GetOrdinal / Exception nie jest tak wrażliwa na liczbę kolumn i zaczyna przewyższać metodę zapętlania około 11 kolumn.
To powiedziawszy, tak naprawdę nie mam preferencji pod względem wydajności, ponieważ 11 kolumn brzmi rozsądnie, ponieważ średnia liczba kolumn zwrócona w całej aplikacji. W obu przypadkach mówimy tutaj o ułamkach milisekundy.
Jednak z punktu widzenia prostoty kodu i obsługi aliasów prawdopodobnie wybrałbym trasę GetOrdinal.
Oto test w formie linqpad. Zapraszam do repost z własną metodą:
źródło
Ten kod rozwiązuje problemy, które miał Levitikon ze swoim kodem: (zaadaptowano z: [1]: http://msdn.microsoft.com/en-us/library/system.data.datatablereader.getschematable.aspx )
Powodem, dla którego wszystkie te bezużyteczne nazwy kolumn są uzyskiwane, a nie nazwa kolumny z tabeli ... jest to, że otrzymujesz nazwę kolumny schematu (tj. Nazwy kolumn dla tabeli schematów)
UWAGA: wydaje się, że to zwraca tylko nazwę pierwszej kolumny ...
EDYCJA: poprawiony kod, który zwraca nazwę wszystkich kolumn, ale nie można do tego użyć SqlDataReader
źródło
return r.GetSchemaTable().Rows.Cast<DataRow>().Select(x => (string)x["ColumnName"]).ToList();
:)Aby Twój kod był niezawodny i czysty, użyj jednej funkcji rozszerzenia, takiej jak ta:
źródło
Nie zabrałem się też
GetSchemaTable
do pracy, dopóki nie znalazłem w ten sposób .Zasadniczo robię to:
źródło
Columns.Contains
jest rozróżniana wielkość liter btw.źródło
W twojej konkretnej sytuacji (wszystkie procedury mają te same kolumny, z wyjątkiem 1, która ma dodatkową 1 kolumnę), lepiej i szybciej będzie sprawdzać czytnik. Właściwość FieldCount, aby je rozróżnić.
Wiem, że to stary post, ale postanowiłem odpowiedzieć, aby pomóc innym w tej samej sytuacji. możesz także (ze względu na wydajność) mieszać to rozwiązanie z iteracyjnym rozwiązaniem.
źródło
Moja klasa dostępu do danych musi być kompatybilna z poprzednimi wersjami, więc być może próbuję uzyskać dostęp do kolumny w wydaniu, w którym jeszcze jej nie ma w bazie danych. Zwracane są pewne dość duże zestawy danych, więc nie jestem wielkim fanem metody rozszerzenia, która musi iterować kolekcję kolumn DataReader dla każdej właściwości.
Mam klasę narzędzi, która tworzy prywatną listę kolumn, a następnie ma ogólną metodę, która próbuje rozwiązać wartość na podstawie nazwy kolumny i typu parametru wyjściowego.
Następnie mogę tak po prostu wywołać mój kod
źródło
Klucz do całego problemu jest tutaj :
Jeśli odwołane trzy linie (obecnie linie 72, 73 i 74) zostaną usunięte, możesz łatwo sprawdzić
-1
, czy kolumna nie istnieje.Jedynym sposobem na obejście tego przy jednoczesnym zapewnieniu wydajności natywnej jest użycie
Reflection
implementacji opartej na następujących czynnościach:Zastosowania:
Metoda rozszerzenia oparta na odbiciu:
źródło
Możesz także wywołać funkcję GetSchemaTable () w DataReader, jeśli chcesz wyświetlić listę kolumn i nie chcesz otrzymywać wyjątku ...
źródło
Co powiesz na
Prawdopodobnie nie byłoby tak wydajne w pętli
źródło
dr.GetSchemaTable().Columns
zawiera - to nie jest to, czego szukasz.Chociaż nie ma metody ujawnionej publicznie, metoda istnieje w klasie wewnętrznej,
System.Data.ProviderBase.FieldNameLookup
któraSqlDataReader
opiera.Aby uzyskać do niego dostęp i uzyskać natywną wydajność, musisz użyć ILGenerator, aby utworzyć metodę w czasie wykonywania. Poniższy kod daje bezpośredni dostęp do
int IndexOf(string fieldName)
wSystem.Data.ProviderBase.FieldNameLookup
klasie, a także wykonać księgowości, żeSqlDataReader.GetOrdinal()
robi tak, że nie ma efekt uboczny. Wygenerowany kod odzwierciedla istniejący,SqlDataReader.GetOrdinal()
z wyjątkiem tego, że wywołujeFieldNameLookup.IndexOf()
zamiastFieldNameLookup.GetOrdinal()
.GetOrdinal()
Metoda zwraca się doIndexOf()
funkcji i zgłasza wyjątek, jeśli-1
jest zwracany, więc obejścia tego zachowania.źródło
ta praca dla mnie
źródło
możesz uzyskać więcej szczegółów tutaj: czy możesz uzyskać nazwy kolumn z SqlDataReader?
źródło