我們首先了解了 ORM 的繫結物件屬性與欄位時使用的方式,只是還有個問題,就是我們原先用的屬性型別都是 string,很好處理沒錯,但大多數的資料結構沒這麼簡單,一定不會只有 string 型別,像 int, double, long, char, boolean 這些型別也時常出現,那麼要怎麼處理這些型別間的轉換?
我們首先了解了 ORM 的繫結物件屬性與欄位時使用的方式,只是還有個問題,就是我們原先用的屬性型別都是 string,很好處理沒錯,但大多數的資料結構沒這麼簡單,一定不會只有 string 型別,像 int, double, long, char, boolean 這些型別也時常出現,那麼要怎麼處理這些型別間的轉換?
為了要實驗這個特性,我們定義了一個叫 CustomerAmount 的類別,使用不同資料型別,這個類別的定義如下:
public class CustomerAmount
{
public string CustomerID { get; set; }
public int Qty { get; set; }
public decimal Amount { get; set; }
}
裡面我們放了 int 和 decimal 的型別,以對於 Customer 的訂單彙總數據,SQL 指令以及資料存取邏輯為:
namespace ConsoleApplication2
{
class ProgramStep2
{
static void Main(string[] args)
{
// step 2. handling data type convert.
SqlConnection db = new SqlConnection("initial catalog=Northwind; integrated security=SSPI");
SqlCommand dbcmd = new SqlCommand(
@"SELECT o.CustomerID,
SUM(od.Quantity) AS Qty,
SUM(od.Quantity * od.UnitPrice * od.Discount) AS Amount
FROM Orders o INNER JOIN [Order Details] od ON o.OrderID = od.OrderID
GROUP BY o.CustomerID", db);
List<CustomerAmount> customerAmounts = new List<CustomerAmount>();
db.Open();
SqlDataReader reader = dbcmd.ExecuteReader(CommandBehavior.CloseConnection);
while (reader.Read())
{
CustomerAmount customerAmount = new CustomerAmount();
for (int i = 0; i < reader.FieldCount; i++)
{
// TODO: bind data into property.
}
customerAmounts.Add(customerAmount);
}
reader.Close();
db.Close();
foreach (CustomerAmount customer in customerAmounts)
{
Console.WriteLine(
"id: {0}, qty: {1:###,##0}, amount: {2:$###,###,##0}",
customer.CustomerID, customer.Qty, customer.Amount);
}
Console.WriteLine("");
Console.WriteLine("Press ENTER to exit.");
Console.ReadLine();
}
}
}
現在的重點是在 TODO 的那一段要怎麼寫,我們一般使用的 DataReader/DataTable 多半是弱型別,也就是 object 型別,而類別屬性是強型別,我們必須要想一套方法來做這件事,但又不想要回到 hard coding 的作法,我們一樣要使用 Reflection,然而型別轉換這件事要考量的因素也不少,像是資料可能會有 NULL 值,轉型可能會出現 InvalidCastException,轉型失敗的預設值,轉型需要的效能損耗等,只是因為資料來源本身就是弱型別,不轉也不行,不過我們想要轉得簡單,不用像這樣一行一行判斷:
PropertyInfo property = customerAmount.GetType().GetProperty(reader.GetName(i));
Type propType = property.PropertyType;
object defaultValue = null;
if (propType == typeof(int) || propType == typeof(long) || propType == typeof(short))
defaultValue = 0;
else if (propType == typeof(float) || propType == typeof(double) || propType == typeof(decimal))
defaultValue = 0.0;
else if (propType == typeof(bool))
defaultValue = false;
else if (propType == typeof(char))
defaultValue = (char)0;
else
defaultValue = string.Empty;
....
所以,我們定義了一個叫 ITypeConverter 的介面,並定義了 StringConverter,IntegerConverter 以及 DecimalConverter 類別:
public class StringConverter : ITypeConverter
{
public object Convert(object ValueToConvert)
{
if (ValueToConvert == null || ValueToConvert == DBNull.Value)
return string.Empty;
return ValueToConvert.ToString();
}
}
public class IntegerConverter : ITypeConverter
{
public object Convert(object ValueToConvert)
{
if (ValueToConvert == null || ValueToConvert == DBNull.Value)
return 0;
return System.Convert.ToInt32(ValueToConvert);
}
}
public class DecimalConverter : ITypeConverter
{
public object Convert(object ValueToConvert)
{
if (ValueToConvert == null || ValueToConvert == DBNull.Value)
return 0.0m;
return System.Convert.ToDecimal(ValueToConvert);
}
}
我們將型別的轉換交給特定的 TypeConverter 以後,用戶端程式要做的就變簡單了,不過因為要動態叫用正確的 Type Converter,所以我們還需要一個 Factory:
public class TypeConverterFactory
{
public static ITypeConverter GetConvertType<T>()
{
if (typeof(T) == typeof(int))
return (new IntegerConverter());
if (typeof(T) == typeof(long))
return (new LongConverter());
if (typeof(T) == typeof(short))
return (new ShortConverter());
if (typeof(T) == typeof(float))
return (new FloatConverter());
if (typeof(T) == typeof(double))
return (new DoubleConverter());
if (typeof(T) == typeof(decimal))
return (new DecimalConverter());
if (typeof(T) == typeof(bool))
return (new BooleanConverter());
if (typeof(T) == typeof(char))
return (new CharConverter());
if (typeof(T) == typeof(string))
return (new StringConverter());
return null;
}
public static ITypeConverter GetConvertType(Type T)
{
if (T == typeof(int))
return (new IntegerConverter());
if (T == typeof(long))
return (new LongConverter());
if (T == typeof(short))
return (new ShortConverter());
if (T == typeof(float))
return (new FloatConverter());
if (T == typeof(double))
return (new DoubleConverter());
if (T == typeof(decimal))
return (new DecimalConverter());
if (T == typeof(bool))
return (new BooleanConverter());
if (T == typeof(char))
return (new CharConverter());
if (T == typeof(string))
return (new StringConverter());
return null;
}
}
最後,再將它拿到用戶端程式來用:
while (reader.Read())
{
CustomerAmount customerAmount = new CustomerAmount();
for (int i = 0; i < reader.FieldCount; i++)
{
PropertyInfo property = customerAmount.GetType().GetProperty(reader.GetName(i));
Type propType = property.PropertyType;
TypeConverters.ITypeConverter typeConverter =
TypeConverters.TypeConverterFactory.GetConvertType(propType);
property.SetValue(customerAmount,
Convert.ChangeType(typeConverter.Convert(reader.GetValue(i)), propType), null);
}
customerAmounts.Add(customerAmount);
}
這樣,就可以省去自己做 Type Convert 的工了。
Source Code Download: https://dotblogsfile.blob.core.windows.net/user/regionbbs/1111/20111110143952457.rar