[C#] SqlDataReader 개체에서 열 이름 확인

열이 존재하는지 확인하는 방법 SqlDataReader개체에 ? 내 데이터 액세스 계층에서 여러 저장 프로 시저 호출에 대해 동일한 개체를 작성하는 메서드를 만들었습니다. 저장 프로 시저 중 하나에 다른 저장 프로 시저에서 사용하지 않는 추가 열이 있습니다. 모든 시나리오에 맞게 방법을 수정하고 싶습니다.

내 응용 프로그램은 C #으로 작성되었습니다.



답변

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;
    }
}

Exception다른 답변과 마찬가지로 제어 논리에 s를 사용하는 것은 나쁜 습관 으로 간주됩니다 성능 비용이 있습니다. 또한 발생 된 예외 # 개에 대해 오탐 (false positive)을 전송하고 예외 발생시 디버거를 설정하는 사람이 누구든지 도와줍니다.

GetSchemaTable ()도 많은 답변에서 또 다른 제안입니다. 모든 버전에서 구현되지 않았기 때문에 필드의 존재 여부를 확인하는 바람직한 방법은 아닙니다 (추상적이고 일부 버전의 dotnetcore에서는 NotSupportedException이 발생 함). GetSchemaTable은 소스체크 아웃 하면 꽤 무거운 기능이기 때문에 성능이 지나치게 뛰어납니다 .

필드를 많이 사용하면 필드를 루핑하면 성능이 약간 저하 될 수 있으며 결과 캐싱을 고려할 수 있습니다.


답변

이 부울 함수를 사용하는 것이 훨씬 좋습니다.

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;
}


답변

최선의 방법은 DataReader에서 GetOrdinal ( “columnName”) 을 먼저 호출 하고 열이 없으면 IndexOutOfRangeException을 잡는 것입니다.

실제로 확장 방법을 만들어 봅시다 :

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

편집하다

좋아,이 게시물은 최근에 몇 개의 다운 투표권을 얻었습니다. 허용 된 답변이기 때문에 삭제할 수 없으므로 업데이트 할 것이고 예외 처리 사용을 정당화하려고합니다. 제어 흐름.

Chad Grant가 게시 한 다른 방법은 DataReader의 각 필드를 반복하고 원하는 필드 이름을 대소 문자를 구분하지 않고 비교하는 것입니다. 이것은 실제로 잘 작동하며 실제로 위의 방법보다 성능이 좋을 것입니다. 확실히 나는 퍼포먼스가 문제가 된 루프 내에서 위의 방법을 사용하지 않을 것입니다.

루프가 작동하지 않는 try / GetOrdinal / catch 메소드가 작동하는 상황을 생각할 수 있습니다. 그러나 지금은 완전히 가상적인 상황이므로 매우 어설픈 정당화입니다. 어쨌든, 나와 함께 견디고 당신의 생각을보십시오.

테이블 내에서 열을 “별칭”으로 만들 수있는 데이터베이스를 상상해보십시오. “EmployeeName”이라는 열이있는 테이블을 정의 할 수 있지만 “EmpName”의 별칭을 제공 할 수 있으며 두 이름 중 하나를 선택하면 해당 열의 데이터가 반환됩니다. 지금까지 나와 함께?

이제 해당 데이터베이스에 대한 ADO.NET 공급자가 있고 열 별칭을 고려한 IDataReader 구현을 코딩했다고 가정합니다.

이제 dr.GetName(i)Chad의 답변에 사용 된 것처럼 단일 문자열 만 반환 할 수 있으므로 열의 “별칭” 중 하나만 반환해야합니다 . 그러나이 GetOrdinal("EmpName")공급자 필드의 내부 구현을 사용하여 원하는 이름의 각 열 별명을 확인할 수 있습니다.

이 가상의 “별칭 된 열”상황에서 try / GetOrdinal / catch 메서드는 결과 집합에서 열 이름의 모든 변형을 확인하는 유일한 방법입니다.

얇은? 확실한. 그러나 생각할 가치가 있습니다. 솔직히 IDataRecord에 대한 “공식적인”HasColumn 메소드를 사용하고 싶습니다.


답변

한 줄에서 DataReader 검색 후 이것을 사용하십시오.

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

그때,

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

편집하다

스키마를로드 할 필요가없는 훨씬 효율적인 단일 라이너 :

var exists = Enumerable.Range(0, dr.FieldCount).Any(i => string.Equals(dr.GetName(i), fieldName, StringComparison.OrdinalIgnoreCase));


답변

Jasmin의 아이디어에 대한 실제 샘플은 다음과 같습니다.

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

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

}


답변

이것은 나를 위해 작동합니다 :

bool hasColumnName = reader.GetSchemaTable().AsEnumerable().Any(c => c["ColumnName"] == "YOUR_COLUMN_NAME");


답변

다음은 간단하고 나를 위해 일했습니다.

 bool hasMyColumn = (reader.GetSchemaTable().Select("ColumnName = 'MyColumnName'").Count() == 1);