Во-первых, позвольте мне объяснить текущую ситуацию: я читаю записи из базы данных и помещаю их в объект для последующего использования; сегодня возник вопрос о преобразовании типа базы данных в тип C# (приведение?).
Давайте посмотрим пример:
namespace Test
{
using System;
using System.Data;
using System.Data.SqlClient;
public enum MyEnum
{
FirstValue = 1,
SecondValue = 2
}
public class MyObject
{
private String field_a;
private Byte field_b;
private MyEnum field_c;
public MyObject(Int32 object_id)
{
using (SqlConnection connection = new SqlConnection("connection_string"))
{
connection.Open();
using (SqlCommand command = connection.CreateCommand())
{
command.CommandText = "sql_query";
using (SqlDataReader reader = command.ExecuteReader(CommandBehavior.SingleRow))
{
reader.Read();
this.field_a = reader["field_a"];
this.field_b = reader["field_b"];
this.field_c = reader["field_c"];
}
}
}
}
}
}
Это (очевидно) терпит неудачу, потому что три вызова this.field_x = reader["field_x"];
вызывают ошибку компилятора Cannot implicitly convert type 'object' to 'xxx'. An explicit conversion exists (are you missing a cast?).
.
Чтобы исправить это, я на данный момент знаю два способа (давайте воспользуемся примером field_b
): номер один — this.field_b = (Byte) reader["field_b"];
, а номер два — this.field_b = Convert.ToByte(reader["field_b"]);
.
Проблема с вариантом номер один заключается в том, что поля DBNull
генерируют исключения при сбое приведения (даже с типами, допускающими значение NULL, такими как String
), а проблема с номером два заключается в том, что он не сохраняет нулевые значения (Convert.ToString(DBNull)
дает String.Empty
), и я нельзя использовать их и с перечислениями.
Итак, после пары поисков в Интернете и здесь, в StackOverflow, я придумал следующее:
public static class Utilities
{
public static T FromDatabase<T>(Object value) where T: IConvertible
{
if (typeof(T).IsEnum == false)
{
if (value == null || Convert.IsDBNull(value) == true)
{
return default(T);
}
else
{
return (T) Convert.ChangeType(value, typeof(T));
}
}
else
{
if (Enum.IsDefined(typeof(T), value) == false)
{
throw new ArgumentOutOfRangeException();
}
return (T) Enum.ToObject(typeof(T), value);
}
}
}
Таким образом, я должен обрабатывать каждый случай.
Вопрос: я что-то упустил? Делаю ли я вомбат (пустая трата денег, мозгов и времени), поскольку есть более быстрый и чистый способ сделать это? Все правильно? Выгода?