[.net] DBNull을 확인한 다음 변수에 할당하는 가장 효율적인 방법은 무엇입니까?


Answers

다음 방법을 사용해야합니다.

Convert.IsDBNull()

프레임 워크에 내장되어 있다는 점을 감안할 때, 이것이 가장 효율적이라고 생각합니다.

나는 다음과 같은 것을 제안 할 것이다.

int? myValue = (Convert.IsDBNull(row["column"]) ? null : (int?) Convert.ToInt32(row["column"]));

그리고 네, 컴파일러가 캐시해야합니다.

Question

이 질문은 가끔씩 나오지만 만족스러운 답을 찾지 못했습니다.

일반적인 패턴은 다음과 같습니다 (행은 DataRow 임).

 if (row["value"] != DBNull.Value)
 {
      someObject.Member = row["value"];
 }

내 첫 번째 질문은 더 효율적입니다 (조건을 뒤집어 버렸습니다) :

  row["value"] == DBNull.Value; // Or
  row["value"] is DBNull; // Or
  row["value"].GetType() == typeof(DBNull) // Or... any suggestions?

This .GetType ()이 빠르다는 것을 나타내지 만, 컴파일러는 내가하지 못하는 몇 가지 트릭을 알고있을 것입니다.

두 번째 질문은 row [ "value"]의 값을 캐싱 할 가치가 있습니까? 아니면 컴파일러가 인덱서를 멀리 최적화합니까?

예 :

  object valueHolder;
  if (DBNull.Value == (valueHolder = row["value"])) {}

노트:

  1. 행 [ "value"]이 (가) 있습니다.
  2. 컬럼의 컬럼 인덱스를 알지 못합니다 (따라서 컬럼 이름 조회).
  3. 특히 DBNull을 확인한 다음 과제 (조숙 한 최적화 등이 아닌)에 대해 구체적으로 묻습니다.

몇 가지 시나리오 (초 단위의 시간, 10,000,000 번의 시도)를 벤치마킹했습니다.

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의 성능은 "=="과 같습니다.

가장 흥미로운 결과? 대 / 소문자 별 (예 : "값"대신 "값") 열의 이름이 불일치하면 문자열에 대해 약 10 배 더 오래 걸립니다.

row["Value"] == DBNull.Value: 00:00:12.2792374

이야기의 도덕은 인덱스로 열을 검색 할 수없는 경우 인덱서에 공급하는 열 이름이 DataColumn의 이름과 정확하게 일치하는지 확인하는 것입니다.

값을 캐싱하는 것이 거의 두 배 빠른 것처럼 보입니다.

No Caching: 00:00:03.0996622
With Caching: 00:00:01.5659920

따라서 가장 효율적인 방법 은 다음과 같습니다 .

 object temp;
 string variable;
 if (DBNull.Value != (temp = row["value"]))
 {
      variable = temp.ToString();
 }



객체가 문자열 일 수있는 까다로운 경우가 있습니다. 아래의 확장 메소드 코드는 모든 경우를 처리합니다. 사용 방법은 다음과 같습니다.

    static void Main(string[] args)
    {
        object number = DBNull.Value;

        int newNumber = number.SafeDBNull<int>();

        Console.WriteLine(newNumber);
    }



    public static T SafeDBNull<T>(this object value, T defaultValue) 
    {
        if (value == null)
            return default(T);

        if (value is string)
            return (T) Convert.ChangeType(value, typeof(T));

        return (value == DBNull.Value) ? defaultValue : (T)value;
    } 

    public static T SafeDBNull<T>(this object value) 
    { 
        return value.SafeDBNull(default(T)); 
    } 



이것이 내가 DataRow에서 읽는 것을 처리하는 방법이다.

///<summary>
/// Handles operations for Enumerations
///</summary>
public static class DataRowUserExtensions
{
    /// <summary>
    /// Gets the specified data row.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="dataRow">The data row.</param>
    /// <param name="key">The key.</param>
    /// <returns></returns>
    public static T Get<T>(this DataRow dataRow, string key)
    {
        return (T) ChangeTypeTo<T>(dataRow[key]);
    }

    private static object ChangeTypeTo<T>(this object value)
    {
        Type underlyingType = typeof (T);
        if (underlyingType == null)
            throw new ArgumentNullException("value");

        if (underlyingType.IsGenericType && underlyingType.GetGenericTypeDefinition().Equals(typeof (Nullable<>)))
        {
            if (value == null)
                return null;
            var converter = new NullableConverter(underlyingType);
            underlyingType = converter.UnderlyingType;
        }

        // Try changing to Guid  
        if (underlyingType == typeof (Guid))
        {
            try
            {
                return new Guid(value.ToString());
            }
            catch

            {
                return null;
            }
        }
        return Convert.ChangeType(value, underlyingType);
    }
}

사용 예 :

if (dbRow.Get<int>("Type") == 1)
{
    newNode = new TreeViewNode
                  {
                      ToolTip = dbRow.Get<string>("Name"),
                      Text = (dbRow.Get<string>("Name").Length > 25 ? dbRow.Get<string>("Name").Substring(0, 25) + "..." : dbRow.Get<string>("Name")),
                      ImageUrl = "file.gif",
                      ID = dbRow.Get<string>("ReportPath"),
                      Value = dbRow.Get<string>("ReportDescription").Replace("'", "\'"),
                      NavigateUrl = ("?ReportType=" + dbRow.Get<string>("ReportPath"))
                  };
}

소품을 괴물 에게 내 .Net ChageTypeTo 코드가 있습니다.




나는 확장 메소드와 비슷한 것을했다. 내 코드는 다음과 같습니다.

public static class DataExtensions
{
    /// <summary>
    /// Gets the value.
    /// </summary>
    /// <typeparam name="T">The type of the data stored in the record</typeparam>
    /// <param name="record">The record.</param>
    /// <param name="columnName">Name of the column.</param>
    /// <returns></returns>
    public static T GetColumnValue<T>(this IDataRecord record, string columnName)
    {
        return GetColumnValue<T>(record, columnName, default(T));
    }

    /// <summary>
    /// Gets the value.
    /// </summary>
    /// <typeparam name="T">The type of the data stored in the record</typeparam>
    /// <param name="record">The record.</param>
    /// <param name="columnName">Name of the column.</param>
    /// <param name="defaultValue">The value to return if the column contains a <value>DBNull.Value</value> value.</param>
    /// <returns></returns>
    public static T GetColumnValue<T>(this IDataRecord record, string columnName, T defaultValue)
    {
        object value = record[columnName];
        if (value == null || value == DBNull.Value)
        {
            return defaultValue;
        }
        else
        {
            return (T)value;
        }
    }
}

그것을 사용하려면, 당신은 뭔가를 할 것입니다.

int number = record.GetColumnValue<int>("Number",0)



이 작업을 수행 한 것은 아니지만 이중 인덱서 호출을 처리하고 정적 / 확장 메서드를 사용하여 코드를 깨끗하게 유지할 수 있습니다.

예.

public static IsDBNull<T>(this object value, T default)
{
    return (value == DBNull.Value)
        ? default
        : (T)value;
}

public static IsDBNull<T>(this object value)
{
    return value.IsDBNull(default(T));
}

그때:

IDataRecord record; // Comes from somewhere

entity.StringProperty = record["StringProperty"].IsDBNull<string>(null);
entity.Int32Property = record["Int32Property"].IsDBNull<int>(50);

entity.NoDefaultString = record["NoDefaultString"].IsDBNull<string>();
entity.NoDefaultInt = record["NoDefaultInt"].IsDBNull<int>();

또한 한 곳에서 null 검사 논리를 유지하는 이점이 있습니다. 단점은 물론 이것이 추가 메서드 호출이라는 것입니다.

그냥 생각.




public static class DBH
{
    /// <summary>
    /// Return default(T) if supplied with DBNull.Value
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="value"></param>
    /// <returns></returns>
    public static T Get<T>(object value)
    {   
        return value == DBNull.Value ? default(T) : (T)value;
    }
}

이런 식으로 사용하십시오.

DBH.Get<String>(itemRow["MyField"])



C #에서 다음 코드를 사용합니다 ( VB.NET 은 단순하지 않습니다).

이 코드는 null / DBNull이 아닌 경우 값을 할당하고, 그렇지 않으면 컴파일러가 할당을 무시할 수 있도록 LHS 값으로 설정할 수있는 기본값을 지정합니다.

oSomeObject.IntMemeber = oRow["Value"] as int? ?? iDefault;
oSomeObject.StringMember = oRow["Name"] as string ?? sDefault;



Links