Получение & ldquo; System.IndexOutOfRangeException & rdquo; [Дубликат]

Доступ к файлам, доступным через SparkContext.addFile--files), можно получить через SparkFiles. Он предоставляет два метода:

  • getDirectory() - возвращает корневой каталог для распределенных файлов
  • get(filename) - возвращает абсолютный путь к файлу

Я не уверен, существуют ли какие-то ограничения Dataproc, но что-то вроде этого должно работать нормально:

from pyspark import SparkFiles

with open(SparkFiles.get('test.yml')) as test_file:
    logging.info(test_file.read())
193
задан Michael Kniskern 2 March 2012 в 00:24
поделиться

23 ответа

Использование Exception s для управляющей логики, как и в некоторых других ответах, считается плохой практикой и имеет издержки на производительность.

Проникновение по полям может привести к небольшому результату, если вы используете его много, и вы может хотеть рассмотреть кеширование результатов

. Более подходящий способ сделать это:

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;
    }
}
297
ответ дан Alexander Abakumov 18 August 2018 в 12:31
поделиться
  • 1
    Что делать, если используется псевдоним? Сравнение имен не будет выполнено. – Murphybro2 24 March 2017 в 11:54

Гораздо лучше использовать эту логическую функцию:

r.GetSchemaTable().Columns.Contains(field)
Один вызов - никаких исключений. Это может вызвать исключения изнутри, но я так не думаю.

ПРИМЕЧАНИЕ. В комментариях ниже мы поняли это ... правильный код на самом деле:

public static bool HasColumn(DbDataReader Reader, string ColumnName) { 
    foreach (DataRow row in Reader.GetSchemaTable().Rows) { 
        if (row["ColumnName"].ToString() == ColumnName) 
            return true; 
    } //Still here? Column not found. 
    return false; 
}
65
ответ дан ampersandre 18 August 2018 в 12:31
поделиться
  • 1
    @Jasmine: Я говорил слишком рано! Ваш код проверяет столбец в таблице схемы, а не ваш результат. Вам нужно сравнить поле " (при условии, что поле «-» - это имя столбца) к значению «ColumnName» каждой строки. поле. Перерыв, когда вы его найдете, верните false, если вы этого не сделаете. – Steve J 15 June 2009 в 22:46
  • 2
    @Steve J: Когда в наборе результатов не будет столбца в GetSchemaTable? – Bless Yahu 7 August 2009 в 17:13
  • 3
    @Jasmine @Steve Так работает ли этот метод вообще? – bzlm 8 April 2010 в 07:39
  • 4
    Для кого-то еще смущенного, ЭТО НЕ РАБОТАЕТ. См. Ответ ниже о том, как получить строку ColumnName из таблицы схемы и использовать ее. – Jason Jackson 17 December 2012 в 19:30
  • 5
    Да, это НЕ РАБОТАЕТ. Кто его так много воздал? Это спасло бы меня много времени отладки, если бы этого ответа не было! – c00000fd 15 April 2013 в 11:11

Ниже приведен пример linq версии принятого ответа:

Enumerable.Range(0, reader.FieldCount).Any(i => reader.GetName(i) == "COLUMN_NAME_GOES_HERE")
4
ответ дан Clement 18 August 2018 в 12:31
поделиться

Я думаю, что лучше всего называть GetOrdinal ("columnName") на вашем DataReader спереди и уловить исключение IndexOutOfRangeException, если столбец отсутствует.

На самом деле, давайте сделаем метод расширения:

public static bool HasColumn(this IDataRecord r, string columnName)
{
    try
    {
        return r.GetOrdinal(columnName) >= 0;
    }
    catch (IndexOutOfRangeException)
    {
        return false;
    }
}

Edit

Хорошо, этот пост начинает собирать несколько проголосовавших в последнее время, и я не могу удалить его, потому что это принятый ответ, поэтому я собираюсь обновить его и (надеюсь) попытаться оправдать использование обработки исключений в качестве потока управления.

Другой способ достижения этого, поскольку Chad Grant , состоит в том, чтобы перебирать каждое поле в DataReader и проводить сравнение без учета регистра для имени поля, которое вы ищете. Это будет работать очень хорошо, и, по правде говоря, он будет работать лучше, чем мой метод выше. Конечно, я бы никогда не использовал метод выше внутри цикла, где performace была проблемой.

Я могу думать об одной ситуации, в которой метод try / GetOrdinal / catch будет работать там, где этого цикла нет. Это сейчас, однако, совершенно гипотетическая ситуация, так что это очень надуманное оправдание. Независимо от того, медведь со мной и посмотреть, что вы думаете.

Представьте базу данных, которая позволила вам столбцы «псевдоним» в таблице. Представьте, что я мог бы определить таблицу с столбцом «EmployeeName», но также дать ей псевдоним «EmpName», а выбор для любого имени вернет данные в этом столбце.

Теперь представьте, что для этой базы данных есть поставщик ADO.NET, и они закодировали для него реализацию IDataReader, которая учитывает псевдонимы столбцов.

Теперь dr.GetName(i) (как используется в ответе Чада) может возвращать только одну строку, поэтому она должна возвращать только one из «псевдонимов» в столбце. Тем не менее, GetOrdinal("EmpName") может использовать внутреннюю реализацию полей этого провайдера для проверки псевдонима каждого столбца для имени, которое вы ищете.

В этой гипотетической ситуации с «псевдонимом столбцов» try / GetOrdinal / catch метод был бы единственным способом убедиться в том, что вы проверяете каждое изменение имени столбца в наборе результатов.

Flimsy? Конечно. Но стоит подумать. Честно говоря, я бы предпочел «официальный» метод HasColumn в IDataRecord.

30
ответ дан Community 18 August 2018 в 12:31
поделиться
  • 1
    Я собирался предложить то же самое, GetOrdinal отлично, потому что поиск нечувствителен к регистру, если он терпит неудачу, он делает чувствительный к регистру поиск. – kd7 17 December 2008 в 01:16
  • 2
    используя исключения для логики управления? нет нет нет – Chad Grant 2 May 2009 в 00:42
  • 3
    Есть одна маленькая вещь, которую каждый забывает, когда я изначально разместил этот вопрос ... Я задал вопрос 12/8/08, и Мэтт опубликовал свой ответ 12/17/08. Все ввязались в ловушку исключения для логики управления, но не предоставили надежного альтернативного решения до 5/1/09. Вот почему он был первоначально отмечен как ответ. Я все еще использую это решение сегодня. – Michael Kniskern 14 December 2010 в 16:32
  • 4
    Это будет иметь производительность, только если столбец не был там. В других описанных методах каждый раз ударяется производительность и увеличивается производительность. Хотя, как правило, плохой практикой избегать использования обработки исключений для потока управления, это решение не должно исключаться, если не считать, что он работает в вашем случае. – Nick Harrison 5 May 2011 в 16:22
  • 5
    +1. Я в порядке с & quot; Не используйте исключение для логики управления & quot; как широкое правило проектирования. Это не означает «избегать его любой ценой». Ответ - очень хорошо документированный обходной путь, и, как пишет @Nick, поражение производительности (если оно есть ..) возникает только тогда, когда столбец не существует. – Larry 4 November 2013 в 15:26

Вы также можете вызвать GetSchemaTable () на вашем DataReader, если вам нужен список столбцов, и вы не хотите получать исключение ...

0
ответ дан Dave Markle 18 August 2018 в 12:31
поделиться

Я также не работал GetSchemaTable, пока не нашел таким образом .

В основном я это делаю:

Dim myView As DataView = dr.GetSchemaTable().DefaultView
myView.RowFilter = "ColumnName = 'ColumnToBeChecked'"

If myView.Count > 0 AndAlso dr.GetOrdinal("ColumnToBeChecked") <> -1 Then
  obj.ColumnToBeChecked = ColumnFromDb(dr, "ColumnToBeChecked")
End If
1
ответ дан David Andersson 18 August 2018 в 12:31
поделиться

Несмотря на отсутствие открытого метода, во внутреннем классе System.Data.ProviderBase.FieldNameLookup, который полагается SqlDataReader, существует метод.

Чтобы получить доступ к нему и получить собственную производительность, вы должны использовать ILGenerator для создания метода во время выполнения. Следующий код предоставит вам прямой доступ к int IndexOf(string fieldName) в классе System.Data.ProviderBase.FieldNameLookup, а также выполнит книгу, сохраняя при этом, что SqlDataReader.GetOrdinal() делает так, что побочный эффект отсутствует. Сгенерированный код отражает существующие SqlDataReader.GetOrdinal(), за исключением того, что он вызывает FieldNameLookup.IndexOf() вместо FieldNameLookup.GetOrdinal(). Метод GetOrdinal() вызывает функцию IndexOf() и выдает исключение, если возвращается -1, поэтому мы обходим это поведение.

using System;
using System.Data;
using System.Data.SqlClient;
using System.Reflection;
using System.Reflection.Emit;

public static class SqlDataReaderExtensions {

   private delegate int IndexOfDelegate(SqlDataReader reader, string name);
   private static IndexOfDelegate IndexOf;

   public static int GetColumnIndex(this SqlDataReader reader, string name) {
      return name == null ? -1 : IndexOf(reader, name);
   }

   public static bool ContainsColumn(this SqlDataReader reader, string name) {
      return name != null && IndexOf(reader, name) >= 0;
   }

   static SqlDataReaderExtensions() {
      Type typeSqlDataReader = typeof(SqlDataReader);
      Type typeSqlStatistics = typeSqlDataReader.Assembly.GetType("System.Data.SqlClient.SqlStatistics", true);
      Type typeFieldNameLookup = typeSqlDataReader.Assembly.GetType("System.Data.ProviderBase.FieldNameLookup", true);

      BindingFlags staticflags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Static;
      BindingFlags instflags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance;

      DynamicMethod dynmethod = new DynamicMethod("SqlDataReader_IndexOf", typeof(int), new Type[2]{ typeSqlDataReader, typeof(string) }, true);
      ILGenerator gen = dynmethod.GetILGenerator();
      gen.DeclareLocal(typeSqlStatistics);
      gen.DeclareLocal(typeof(int));

      // SqlStatistics statistics = (SqlStatistics) null;
      gen.Emit(OpCodes.Ldnull);
      gen.Emit(OpCodes.Stloc_0);
      // try {
      gen.BeginExceptionBlock();
      //    statistics = SqlStatistics.StartTimer(this.Statistics);
      gen.Emit(OpCodes.Ldarg_0); //this
      gen.Emit(OpCodes.Call, typeSqlDataReader.GetProperty("Statistics", instflags | BindingFlags.GetProperty, null, typeSqlStatistics, Type.EmptyTypes, null).GetMethod);
      gen.Emit(OpCodes.Call, typeSqlStatistics.GetMethod("StartTimer", staticflags | BindingFlags.InvokeMethod, null, new Type[] { typeSqlStatistics }, null));
      gen.Emit(OpCodes.Stloc_0); //statistics
      //    if(this._fieldNameLookup == null) {
      Label branchTarget = gen.DefineLabel();
      gen.Emit(OpCodes.Ldarg_0); //this
      gen.Emit(OpCodes.Ldfld, typeSqlDataReader.GetField("_fieldNameLookup", instflags | BindingFlags.GetField));
      gen.Emit(OpCodes.Brtrue_S, branchTarget);
      //       this.CheckMetaDataIsReady();
      gen.Emit(OpCodes.Ldarg_0); //this
      gen.Emit(OpCodes.Call, typeSqlDataReader.GetMethod("CheckMetaDataIsReady", instflags | BindingFlags.InvokeMethod, null, Type.EmptyTypes, null));
      //       this._fieldNameLookup = new FieldNameLookup((IDataRecord)this, this._defaultLCID);
      gen.Emit(OpCodes.Ldarg_0); //this
      gen.Emit(OpCodes.Ldarg_0); //this
      gen.Emit(OpCodes.Ldarg_0); //this
      gen.Emit(OpCodes.Ldfld, typeSqlDataReader.GetField("_defaultLCID", instflags | BindingFlags.GetField));
      gen.Emit(OpCodes.Newobj, typeFieldNameLookup.GetConstructor(instflags, null, new Type[] { typeof(IDataReader), typeof(int) }, null));
      gen.Emit(OpCodes.Stfld, typeSqlDataReader.GetField("_fieldNameLookup", instflags | BindingFlags.SetField));
      //    }
      gen.MarkLabel(branchTarget);
      gen.Emit(OpCodes.Ldarg_0); //this
      gen.Emit(OpCodes.Ldfld, typeSqlDataReader.GetField("_fieldNameLookup", instflags | BindingFlags.GetField));
      gen.Emit(OpCodes.Ldarg_1); //name
      gen.Emit(OpCodes.Call, typeFieldNameLookup.GetMethod("IndexOf", instflags | BindingFlags.InvokeMethod, null, new Type[] { typeof(string) }, null));
      gen.Emit(OpCodes.Stloc_1); //int output
      Label leaveProtectedRegion = gen.DefineLabel();
      gen.Emit(OpCodes.Leave_S, leaveProtectedRegion);
      // } finally {
      gen.BeginFaultBlock();
      //    SqlStatistics.StopTimer(statistics);
      gen.Emit(OpCodes.Ldloc_0); //statistics
      gen.Emit(OpCodes.Call, typeSqlStatistics.GetMethod("StopTimer", staticflags | BindingFlags.InvokeMethod, null, new Type[] { typeSqlStatistics }, null));
      // }
      gen.EndExceptionBlock();
      gen.MarkLabel(leaveProtectedRegion);
      gen.Emit(OpCodes.Ldloc_1);
      gen.Emit(OpCodes.Ret);

      IndexOf = (IndexOfDelegate)dynmethod.CreateDelegate(typeof(IndexOfDelegate));
   }

}
0
ответ дан Derek Ziemba 18 August 2018 в 12:31
поделиться

Я написал для пользователей Visual Basic:

Protected Function HasColumnAndValue(ByRef reader As IDataReader, ByVal columnName As String) As Boolean
    For i As Integer = 0 To reader.FieldCount - 1
        If reader.GetName(i).Equals(columnName) Then
            Return Not IsDBNull(reader(columnName))
        End If
    Next

    Return False
End Function

Я думаю, что это более мощно, а использование:

If HasColumnAndValue(reader, "ID_USER") Then
    Me.UserID = reader.GetDecimal(reader.GetOrdinal("ID_USER")).ToString()
End If
7
ответ дан HoLyVieR 18 August 2018 в 12:31
поделиться

В одной строке используйте это после извлечения данных DataReader:

var fieldNames = Enumerable.Range(0, dr.FieldCount).Select(i => dr.GetName(i)).ToArray();

Затем

if (fieldNames.Contains("myField"))
{
    var myFieldValue = dr["myField"];
    ...

Edit

Гораздо более эффективный однострочный что не требует загрузки схемы:

var exists = Enumerable.Range(0, dr.FieldCount).Any(i => string.Equals(dr.GetName(i), fieldName, StringComparison.OrdinalIgnoreCase));
20
ответ дан Larry 18 August 2018 в 12:31
поделиться

Если вы прочитаете вопрос, Майкл спросил о DataReader, а не о DataRecord.

Использование r.GetSchemaTable().Columns.Contains(field) в DataRecord действительно работает, но оно возвращает столбцы BS (см. Снимок экрана ниже).

Чтобы узнать, существует ли столбец данных и содержит данных в DataReader, используйте следующие расширения:

public static class DataReaderExtensions
{
    /// <summary>
    /// Checks if a column's value is DBNull
    /// </summary>
    /// <param name="dataReader">The data reader</param>
    /// <param name="columnName">The column name</param>
    /// <returns>A bool indicating if the column's value is DBNull</returns>
    public static bool IsDBNull(this IDataReader dataReader, string columnName)
    {
        return dataReader[columnName] == DBNull.Value;
    }

    /// <summary>
    /// Checks if a column exists in a data reader
    /// </summary>
    /// <param name="dataReader">The data reader</param>
    /// <param name="columnName">The column name</param>
    /// <returns>A bool indicating the column exists</returns>
    public static bool ContainsColumn(this IDataReader dataReader, string columnName)
    {
        /// See: http://stackoverflow.com/questions/373230/check-for-column-name-in-a-sqldatareader-object/7248381#7248381
        try
        {
            return dataReader.GetOrdinal(columnName) >= 0;
        }
        catch (IndexOutOfRangeException)
        {
            return false;
        }
    }
}

Использование:

    public static bool CanCreate(SqlDataReader dataReader)
    {
        return dataReader.ContainsColumn("RoleTemplateId") 
            && !dataReader.IsDBNull("RoleTemplateId");
    }

Вызов r.GetSchemaTable().Columns в DataReader возвращает столбцы BS:

Calling GetSchemeTable in a DataReader [/g0]

8
ответ дан Levitikon 18 August 2018 в 12:31
поделиться
public static bool DataViewColumnExists(DataView dv, string columnName)
{
    return DataTableColumnExists(dv.Table, columnName);
}

public static bool DataTableColumnExists(DataTable dt, string columnName)
{
    string DebugTrace = "Utils::DataTableColumnExists(" + dt.ToString() + ")";
    try
    {
        return dt.Columns.Contains(columnName);
    }
    catch (Exception ex)
    {
        throw new MyExceptionHandler(ex, DebugTrace);
    }
}

Columns.Contains нечувствителен к регистру.

1
ответ дан LPL 18 August 2018 в 12:31
поделиться

Чтобы ваш код был прочным и чистым, используйте одну функцию расширения, например:

    Public Module Extensions

        <Extension()>
        Public Function HasColumn(r As SqlDataReader, columnName As String) As Boolean

            Return If(String.IsNullOrEmpty(columnName) OrElse r.FieldCount = 0, False, Enumerable.Range(0, r.FieldCount).Select(Function(i) r.GetName(i)).Contains(columnName, StringComparer.OrdinalIgnoreCase))

        End Function

    End Module
1
ответ дан Michael B 18 August 2018 в 12:31
поделиться

Эти ответы уже размещены здесь. Просто немного:

bool b = reader.GetSchemaTable().Rows
                                .Cast<DataRow>()
                                .Select(x => (string)x["ColumnName"])
                                .Contains(colName, StringComparer.OrdinalIgnoreCase);
//or

bool b = Enumerable.Range(0, reader.FieldCount)
                   .Select(reader.GetName)
                   .Contains(colName, StringComparer.OrdinalIgnoreCase);

Второй - чище и намного быстрее. Даже если вы не запускаете GetSchemaTable каждый раз в первом подходе, поиск будет очень медленным.

2
ответ дан nawfal 18 August 2018 в 12:31
поделиться
  • 1
    Или в одной строке return r.GetSchemaTable().Rows.Cast<DataRow>().Select(x => (string)x["ColumnName"]).ToList(); :) – nawfal 12 December 2013 в 16:03

В вашей конкретной ситуации (все процедуры имеют одни и те же столбцы, кроме 1, который имеет дополнительный 1 столбец), будет лучше и быстрее проверять читателя. Свойство FieldCount, чтобы различать их.

const int NormalColCount=.....
if(reader.FieldCount > NormalColCount)
{
// Do something special
}

Я знаю, что это старый пост, но я решил ответить, чтобы помочь другим в той же ситуации. вы также можете (по соображениям производительности) смешать это решение с решением для итерации решения.

0
ответ дан Pablo Jomer 18 August 2018 в 12:31
поделиться
  • 1
    Назовите решение, на которое вы ссылаетесь. Какие два решения следует смешивать? – Pablo Jomer 18 August 2014 в 08:47

Следующее прост и работает для меня:

 bool hasMyColumn = (reader.GetSchemaTable().Select("ColumnName = 'MyColumnName'").Count() == 1);
10
ответ дан Paulo Lisboa 18 August 2018 в 12:31
поделиться

Как насчет

if (dr.GetSchemaTable().Columns.Contains("accounttype"))
   do something
else
   do something

Вероятно, это было бы не так эффективно в цикле

-1
ответ дан Skadoosh 18 August 2018 в 12:31
поделиться

Здесь решение из жасмина в одной строке ... (еще один, простой!):

reader.GetSchemaTable().Select("ColumnName='MyCol'").Length > 0;
5
ответ дан spaark 18 August 2018 в 12:31
поделиться

Вот рабочий пример идеи Ясмина:

var cols = r.GetSchemaTable().Rows.Cast<DataRow>().Select
    (row => row["ColumnName"] as string).ToList(); 

if (cols.Contains("the column name"))
{

}
19
ответ дан Tim Cooper 18 August 2018 в 12:31
поделиться
  • 1
    Только если вы завершите попытку / поймать его – Donald.Record 25 June 2015 в 15:59
  • 2
    Вы можете упростить эту идею с помощью: reader.GetSchemaTable (). Columns.Contains (& quot; myFiled & quot;) – Lev Z 24 November 2016 в 17:24

Мой класс доступа к данным должен быть обратно совместимым, поэтому я могу попытаться получить доступ к столбцу в выпуске, где он еще не существует в базе данных. У нас есть несколько довольно больших наборов данных, которые возвращаются, поэтому я не являюсь большим поклонником метода расширения, который должен перебирать коллекцию столбцов DataReader для каждого свойства.

У меня есть класс утилиты, который создает частный список столбцов, а затем имеет общий метод, который пытается разрешить значение на основе имени столбца и типа выходного параметра.

private List<string> _lstString;

public void GetValueByParameter<T>(IDataReader dr, string parameterName, out T returnValue)
{
    returnValue = default(T);

    if (!_lstString.Contains(parameterName))
    {
        Logger.Instance.LogVerbose(this, "missing parameter: " + parameterName);
        return;
    }

    try
    {
        if (dr[parameterName] != null && [parameterName] != DBNull.Value)
            returnValue = (T)dr[parameterName];
    }
    catch (Exception ex)
    {
        Logger.Instance.LogException(this, ex);
    }
}

/// <summary>
/// Reset the global list of columns to reflect the fields in the IDataReader
/// </summary>
/// <param name="dr">The IDataReader being acted upon</param>
/// <param name="NextResult">Advances IDataReader to next result</param>
public void ResetSchemaTable(IDataReader dr, bool nextResult)
{
    if (nextResult)
        dr.NextResult();

    _lstString = new List<string>();

    using (DataTable dataTableSchema = dr.GetSchemaTable())
    {
        if (dataTableSchema != null)
        {
            foreach (DataRow row in dataTableSchema.Rows)
            {
                _lstString.Add(row[dataTableSchema.Columns["ColumnName"]].ToString());
            }
        }
    }
}

Тогда я могу просто вызвать мой код так:

using (var dr = ExecuteReader(databaseCommand))
{
    int? outInt;
    string outString;

    Utility.ResetSchemaTable(dr, false);        
    while (dr.Read())
    {
        Utility.GetValueByParameter(dr, "SomeColumn", out outInt);
        if (outInt.HasValue) myIntField = outInt.Value;
    }

    Utility.ResetSchemaTable(dr, true);
    while (dr.Read())
    {
        Utility.GetValueByParameter(dr, "AnotherColumn", out outString);
        if (!string.IsNullOrEmpty(outString)) myIntField = outString;
    }
}
0
ответ дан Tresto 18 August 2018 в 12:31
поделиться

это работает для меня:

bool hasColumnName = reader.GetSchemaTable().AsEnumerable().Any(c => c["ColumnName"] == "YOUR_COLUMN_NAME");
12
ответ дан Victor Labastida 18 August 2018 в 12:31
поделиться
Hashtable ht = new Hashtable();
    Hashtable CreateColumnHash(SqlDataReader dr)
    {
        ht = new Hashtable();
        for (int i = 0; i < dr.FieldCount; i++)
        {
            ht.Add(dr.GetName(i), dr.GetName(i));
        }
        return ht;
    }

    bool ValidateColumn(string ColumnName)
    {
        return ht.Contains(ColumnName);
    }
3
ответ дан Walter Stabosz 18 August 2018 в 12:31
поделиться
2
ответ дан nawfal 6 September 2018 в 23:54
поделиться
2
ответ дан nawfal 30 October 2018 в 04:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: