Извлечение из DataRow или DataReader с одной функцией - PullRequest
5 голосов
/ 09 июня 2011

Я ищу решение, как иметь возможность извлекать данные из базы данных при использовании либо DataRow, либо DataReader только с одной функцией (или одной базовой функцией).

Моя проблема связана с тем фактом, что иногда мне нужен DataReader, а иногда мне нужен DataTable / DataRow, но затем для извлечения данных из этих объектов мне нужны два отдельных метода доступа к данным, потому что они не разделяют интерфейс.

Обычно, когда меняется структура моей базы данных, мне не нужно входить и писать следующий код поиска данных в нескольких функциях:

someValue = dr["someValue"]

Это тот же синтаксис и делает то же самое, поэтому я хочу функцию, которая разделяет эту функциональность независимо от того, использую ли я DataReader или DataTable / DataRow для извлечения данных из базы данных.

Ответы [ 5 ]

5 голосов
/ 09 июня 2011

Вы можете использовать CreateDataReader метод в классе DataTable для доступа к данным через базовый класс DbDataReader. Следовательно, вы можете изменить реализацию, но сохранить отображение.

public List<MyType> GetMyTypeCollection(DbDataReader reader)
{
//mapping code here
}

Было бы лучше, если бы вы могли перейти к ORM, где вам не нужно отображать карту вручную.

Взгляните на этот микро ORM Dapper

2 голосов
/ 09 июня 2011

Используйте эту статью , чтобы преобразовать накопитель данных в таблицу данных, а затем вы можете связать их как таблицу данных

Таким образом, вы в основном добавили бы эту функцию, которая вызывается из вашего dataLayer:

public DataTable ConvertDataReader(SqlDataReader dr)
{
  SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.CloseConnection); 
  DataTable dtSchema = dr.GetSchemaTable();
  DataTable dt = new DataTable();

  // You can also use an ArrayList instead of List<>
  List<DataColumn> listCols = new List<DataColumn>();            
  if(dtSchema != null) 
  {
     foreach (DataRow drow in dtSchema.Rows)
     {
        string columnName = System.Convert.ToString(drow["ColumnName"]); 
        DataColumn column = new DataColumn(columnName, 
                               (Type)(drow["DataType"]));
        column.Unique = (bool)drow["IsUnique"];
        column.AllowDBNull = (bool)drow["AllowDBNull"];
        column.AutoIncrement = (bool)drow["IsAutoIncrement"];
        listCols.Add(column);
        dt.Columns.Add(column);
     }
  }

  // Read rows from DataReader and populate the DataTable 
  while (dr.Read())
  {
    DataRow dataRow = dt.NewRow();
    for(int i = 0; i < listCols.Count; i++)
    {
      dataRow[((DataColumn)listCols[i])] = dr[i];
    }
    dt.Rows.Add(dataRow);
  }
}

И затем в своей функции, где вы получаете набор данных, вы должны сделать, если это dataReader, передать ридер в функцию, чтобы вернуть набор данных:

DataTable dtFromReader = ConvertDataReader(dr);
1 голос
/ 09 июня 2011

Вы имеете в виду автоматическое сопоставление строки результата sql-запроса с сущностью?Как это?

public static List<T> ToList<T>(this IDataReader idr, int count) where T : new()
{
    if (idr == null)
        throw new ArgumentNullException("idr");

    if (idr.IsClosed)
        throw new ArgumentException("IDataReader is closed.");

    Type businessEntityType = typeof(T);
    List<T> entitys = new List<T>();
    Hashtable hashtable = new Hashtable();
    PropertyInfo[] properties = businessEntityType.GetProperties();

    int idx = 0;

    foreach (PropertyInfo info in properties)
    {
        hashtable[info.Name.ToUpper()] = info;
    }

    while (idr.Read())
    {
        if (count > 0)
            idx++;

        T newObject = new T();
        for (int index = 0; index < idr.FieldCount; index++)
        {
            PropertyInfo info = (PropertyInfo)hashtable[idr.GetName(index).ToUpper()];
            if (info != null && info.CanWrite)
            {
                try
                {
                    info.SetValue(newObject, idr.GetValue(index), null);
                }
                catch
                {

                }
            }
        }

        entitys.Add(newObject);

        if (idx > count)
            break;
    }
    return entitys;
}
0 голосов
/ 23 марта 2017

В качестве альтернативного решения я использовал приватную функцию, которая принимала динамический аргумент. Затем добавлены две открытые функции, которые принимают аргументы «DataRow» и «DataReader». Вы можете использовать это, чтобы ограничить вызовы известными типами или интерфейсами.

public MyResults DoStuff(DataRow dr)
{
    return ActualDoStuff(dr);
}

public MyResults DoStuff(DataReader dr) //IDataRecord is better if just reading
{
    return ActualDoStuff(dr);
}

private MyResults ActualDoStuff(dynamic dr)
{
    var rez = new MyResults();
    rez.someValue = dr["someValue"];
    return rez;
}

Слово предупреждения, хотя. Возможно, вам придется немного переписать код загрузки, поскольку поведение содержимого dr, доступного через строку, в динамике немного отличается. То есть

if(dr["someValue"] == DBNull.Value)

может потребоваться изменить на

if(dr["someValue"] is DBNull)

Но этот подход по-прежнему позволяет избежать дублирования проблемы с кодом загрузки.

0 голосов
/ 09 июня 2011

Создайте адаптер, чтобы скрыть реализацию DataReader для работы с DataTable/DataRow кодами

...