2012-09-07 37 views
5

Tôi hiện đang sử dụng phương pháp này để đọc dữ liệu từ DataReader -phương pháp Generic để đọc dữ liệu từ DataReader

private T GetValue<T>(object obj) 
{ 
    if (typeof(DBNull) != obj.GetType()) 
    { 
     return (T)obj; 
    } 
    return default(T); 
} 

gọi phương pháp nêu trên như -

GetValue<int>(dataReader["columnName1"]) 
GetValue<string>(dataReader["columnName2"]) 
GetValue<float>(dataReader["columnName3"]) 

Tuy nhiên điều này không thành công khi columnName3 là có giá trị như 7200000 có lỗi
Invalid Cast Exception.

Tôi đang sửa đổi phương pháp thay thế -

return (T)obj; 

với

return (T)Convert.ChangeType(obj, typeof(T)); 

Nhưng mong kiếm một cách tốt hơn là sự thay đổi này sẽ liên quan đến loại đúc hai lần.
Bất kỳ ý tưởng nào tốt hơn?

Cảm ơn bạn!

+0

datatype gì là columnName3 trong cơ sở dữ liệu? – RQDQ

+0

bạn đã thấy LINQ to Dataset chưa? –

+0

@RQDQ, đó là kiểu dữ liệu SQL Server 'thực'. Tôi chỉ cố gắng để nhân rộng trong giao diện điều khiển ứng dụng với mã sau: đối tượng objValue = 7200000; float floatValue = (float) objValue; // thất bại ở đây – iniki

Trả lời

4

Một phương pháp chung có lợi thế là bạn có thể giảm rất nhiều mã bloat, nhưng nếu không thì việc tung ra trình bao bọc của riêng bạn cho từng loại dữ liệu sẽ cho bạn sự linh hoạt để xử lý tùy chỉnh. Và hầu hết các truy vấn db của bạn sẽ có một hiệu ứng đáng chú ý về hiệu suất hơn so với chế độ truy xuất.

Tôi đề nghị bạn viết một tập hợp các phương pháp mở rộng của riêng bạn hơn là có một cách tiếp cận chung. Mở rộng phương thức trên IDataReader mang lại cho bạn lợi ích của việc không truyền các phương thức trên toàn bộ các loại đối tượng phụ. Tôi đã phải xử lý riêng các loại kể từ khi các kết nối khác nhau hoạt động khác nhau, đặc biệt với loại Guid. Ngoài ra, thật khó để biết liệu nhà quản trị có đọc giá trị 0 hoặc DBNull khi bạn trả lại 0 cho cả hai trường hợp. Cho phép nói rằng có một trường enum trong bảng của bạn với một giá trị null. tại sao bạn lại muốn nó được đọc là enum đầu tiên?

Chỉ cần gọi:

dataReader.GetInt("columnName1") 
dataReader.GetString("columnName3") 
dataReader.GetFloat("columnName3") 

Và các phương pháp:

public static int? GetInt(this IDataReader r, string columnName) 
{ 
    var i = r[columnName];  
    if (i.IsNull()) 
     return null; //or your preferred value 

    return (int)i; 
} 

public static bool IsNull<T>(this T obj) where T : class 
{ 
    return (object)obj == null || obj == DBNull.Value; 
} 

Và tương tự,

public static string GetString(this IDataReader r, string columnName) 
{ 
} 

public static float GetFloat(this IDataReader r, string columnName) 
{ 
} 

Nếu bạn thực sự muốn một hàm tổng quát, bạn có thể có nó quá.

public static T Get<T>(this IDataReader r, string columnName, T defaultValue = default(T)) 
{ 
    var obj = r[columnName];  
    if (obj.IsNull()) 
     return defaultValue; 

    return (T)obj; 
} 

Vì vậy, gọi nó là

dataReader.Get<int>(1); //if DBNull should be treated as 0 
dataReader.Get<int?>(1); //if DBNull should be treated as null 
dataReader.Get<int>(1, -1); //if DBNull should be treated as a custom value, say -1 

Điều đó nói rằng, lỗi là bởi vì bạn không đúc với đúng chủng loại như đã chỉ ra trong ý kiến. Tôi có thể đã sử dụng tính năng kiểm tra được xây dựng trong DBNull, nhưng tôi không tránh đọc dữ liệu nhiều lần từ người đọc, inspired from this curious case of microoptimization

0

Bắt đầu với Khuôn khổ .NET 4.5

static class SqlReaderExtension 
{ 
    public static async Task<T> ReadAsync<T>(this SqlDataReader reader, string fieldName) 
    { 
     if (reader == null) throw new ArgumentNullException(nameof(reader)); 
     if (string.IsNullOrEmpty(fieldName)) 
      throw new ArgumentException("Value cannot be null or empty.", nameof(fieldName)); 

     int idx = reader.GetOrdinal(fieldName); 
     return await reader.GetFieldValueAsync<T>(idx); 
    } 
} 

và sau đó

string result = await reader.ReadAsync<string>("FieldName"); 
Các vấn đề liên quan