С# - IDataReader для сопоставления объектов с использованием дженериков

Как я могу сопоставить объект DataReader в объект класса с помощью дженериков?

Например, мне нужно сделать следующее:

public class Mapper<T>
    {
        public static List<T> MapObject(IDataReader dr)
        {
            List<T> objects = new List<T>();

            while (dr.Read())
            {
                //Mapping goes here...
            }

            return objects;
        }
    }

И позже мне нужно вызвать этот класс-метод следующим образом:

IDataReder dataReader = DBUtil.Fetchdata("SELECT * FROM Book");

List<Book> bookList = Mapper<Book>.MapObject(dataReder);

foreach (Book b in bookList)
{
     Console.WriteLine(b.ID + ", " + b.BookName);
}

Обратите внимание, что класс Mapper должен иметь возможность отображать объект любого типа, представленный T.

Ответ 1

Я использую ValueInjecter для этого

Я делаю вот так:

 while (dr.Read())
  {
      var o = new User();
      o.InjectFrom<DataReaderInjection>(dr);
      yield return o;
  }

вам понадобится этот ValueInjection для этого:

public class DataReaderInjection : KnownSourceValueInjection<IDataReader>
    {
        protected override void Inject(IDataReader source, object target, PropertyDescriptorCollection targetProps)
        {
            for (var i = 0; i < source.FieldCount; i++)
            {
                var activeTarget = targetProps.GetByName(source.GetName(i), true);
                if (activeTarget == null) continue;

                var value = source.GetValue(i);
                if (value == DBNull.Value) continue;

                activeTarget.SetValue(target, value);
            }
        }
    }

Ответ 2

Ну, я не знаю, подходит ли это здесь, но вы можете использовать ключевое слово yield

public static IEnumerable<T> MapObject(IDataReader dr, Func<IDataReader, T> convertFunction)
        {
            while (dr.Read())
            {
                yield return convertFunction(dr);
            }
        }

Ответ 4

Это будет очень сложно сделать по той причине, что вы в основном пытаетесь сопоставить две неизвестные вместе. В вашем общем объекте тип неизвестен, а в вашем datareader таблица неизвестна.

Итак, что бы я предложил, вы создаете какой-то атрибут столбца для присоединения к свойствам вашего объекта. Затем просмотрите эти атрибуты свойств и попытайтесь найти данные из этих атрибутов в datareader.

Ваша самая большая проблема будет, что произойдет, если одно из свойств не будет найдено в читателе, или наоборот, один из столбцов в читателе не будет найден в сущности.

Удачи, но если вы хотите сделать что-то подобное, вы, вероятно, захотите ORM или, по крайней мере, какую-то реализацию Active Record.

Ответ 5

Самый простой способ, с помощью которого я могу думать, - это предоставить делегату Func<T,T> для преобразования каждого столбца и построения вашей книги.

В качестве альтернативы, если вы следовали некоторым соглашениям, вы могли бы обработать это путем отражения. Например, если каждый столбец сопоставлен с свойством в результирующем объекте с использованием того же имени, и вы ограничили T в своем Mapper на создание конструктивного T, вы можете использовать отражение, чтобы установить значение каждого свойства в значение в соответствующем столбце.

Ответ 6

Я не думаю, что вы сможете обойти определение отношений между полями в какой-то форме. Взгляните на эту статью и обратите особое внимание на то, как определено отображение, оно может сработать для вас.

http://www.c-sharpcorner.com/UploadFile/rmcochran/elegant_dal05212006130957PM/elegant_dal.aspx

Ответ 7

как насчет следующего

abstract class DataMapper
{
    abstract public object Map(IDataReader);
}

class BookMapper : DataMapper
{
   override public object Map(IDataReader reader)
   {
       ///some mapping stuff
       return book;
   }
}

public class Mapper<T>
{
    public static List<T> MapObject(IDataReader dr)
    {
        List<T> objects = new List<T>();
        DataMapper myMapper = getMapperFor(T);
        while (dr.Read())
        {
            objects.Add((T)myMapper(dr));
        }

        return objects;
    }

    private DataMapper getMapperFor(T myType)
    {
       //switch case or if or whatever
       ...
       if(T is Book) return bookMapper;

    }
}

Не знаю, синтаксически ли это правильно, но я надеюсь, что у вас возникнет эта идея.

Ответ 8

Как насчет использования Fluent Ado.net?

Ответ 10

Я бы порекомендовал вам использовать AutoMapper для этого.