Допустим, я создаю уровень доступа к данным для приложения. Обычно у меня есть определение класса для каждого типа объекта, который хранится в базе данных. Конечно, при фактическом доступе к данным извлекаются данные в форме носителя данных, типизированного или нетипизированного набора данных или подобного, обычно с данными, необходимыми для создания одного объекта для каждой строки результатов.
Как вы подойдете к созданию экземпляров объектов на уровне данных? Будет ли конструктор, принимающий строку данных? Если да, то как сделать этот тип безопасным? Или у вас должен быть список в конструкторе по одному параметру для каждого поля, которое вы хотите создать, даже если полей может быть много? Вы бы отметили этот конструктор как «внутренний»?





Если вас не устраивают DataRow или SqlDataReader, вам следует взглянуть на систему ORM, такую как Linq to Sql или nHibernate, вместо того, чтобы заново изобретать колесо самостоятельно.
(Кстати, это называется паттерн "ActiveRecord")
Я настоятельно рекомендую вам использовать инструмент ORM. Даже простые проекты могут использовать ORM быстро и незаметно ... в частности, обратите внимание на инструмент ActiveRecordзамок (который находится поверх NHibernate для упрощения объявления модели).
Я добился этого с помощью отражения. Я называю столбец из оператора Select объекта.
Предполагается, что у вас есть шаблонный вспомогательный класс. Если вы хотите нанести его на объект самостоятельно, вы можете просто заменить всю букву T на объект.
Это пример:
private T ObjectFromRow(DataRow row)
{
Type t = typeof(T);
T newObj = (T)Activator.CreateInstance(t);
System.Reflection.PropertyInfo[] properties = t.GetProperties();
for (int i = 0; i < properties.Length; i++)
{
if (!properties[i].CanWrite)
{
continue;
}
if (!row.Table.Columns.Contains(properties[i].Name))
{
continue;
}
if (row[properties[i].Name] == DBNull.Value)
{
continue;
}
if (properties[i].PropertyType == typeof(string))
{
properties[i].SetValue(newObj, row[properties[i].Name], null);
}
else if (properties[i].PropertyType == typeof(double))
{
properties[i].SetValue(newObj, double.Parse(row[properties[i].Name].ToString()), null);
}
else if (properties[i].PropertyType == typeof(int))
{
properties[i].SetValue(newObj, int.Parse(row[properties[i].Name].ToString()), null);
}
else if (properties[i].PropertyType == typeof(DateTime))
{
properties[i].SetValue(newObj, DateTime.Parse(row[properties[i].Name].ToString()), null);
}
else if (properties[i].PropertyType == typeof(bool))
{
properties[i].SetValue(newObj, bool.Parse(row[properties[i].Name].ToString()), null);
}
}
return newObj;
}
@Joel (re: сложные запросы, соединения и т. д.)
Инструмент NHibernate и Castle ActiveRecord может обрабатывать очень сложные запросы и объединения через отношения классов и тщательный класс «Expression» (который вы можете добавить к методам запроса) или использование «языка запросов Hibernate» (HQL).
Вы можете погуглить любую из этих деталей, проверить официальный документация или посмотреть потрясающие скринкасты Лето NHibernate.
В качестве альтернативы NHibernate & Castle вы можете взглянуть на SubSonic. Здесь также используется ActiveRecord, но он больше похож на швейцарский армейский нож, чем на NHibernate.
Обновлено:
Вот образец из документации SubSonic:
Simple Select with string columns
int records = new Select("productID").
From("Products").GetRecordCount();
Assert.IsTrue(records == 77);
Simple Select with typed columns
int records = new Select(Product.ProductIDColumn, Product.ProductNameColumn).
From<Product>().GetRecordCount();
Assert.IsTrue(records == 77);
И еще немного Примеры:
Standard Deviation
const double expected = 42.7698669325723;
// overload #1
double result = new
Select(Aggregate.StandardDeviation("UnitPrice"))
.From(Product.Schema)
.ExecuteScalar<double>();
Assert.AreEqual(expected, result);
// overload #2
result = new
Select(Aggregate.StandardDeviation(Product.UnitPriceColumn))
.From(Product.Schema)
.ExecuteScalar<double>();
Assert.AreEqual(expected, result);
// overload #3
result = new
Select(Aggregate.StandardDeviation("UnitPrice", "CheapestProduct"))
.From(Product.Schema)
.ExecuteScalar<double>();
Assert.AreEqual(expected, result);
// overload #4
result = new
Select(Aggregate.StandardDeviation(Product.UnitPriceColumn, "CheapestProduct"))
.From(Product.Schema)
.ExecuteScalar<double>();
Assert.AreEqual(expected, result);
И немного Методы с подстановочными знаками:
[Test]
public void Select_Using_StartsWith_C_ShouldReturn_9_Records() {
int records = new Select().From<Product>()
.Where(Northwind.Product.ProductNameColumn).StartsWith("c")
.GetRecordCount();
Assert.AreEqual(9, records);
}