С# — сопоставление 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.


person user366312    schedule 09.07.2009    source источник
comment
Одно предложение - читать в IEnumerable‹T› с возвратом доходности.   -  person Daniel A. White    schedule 09.07.2009
comment
//здесь идет сопоставление, именно то, что я показал вам в своем ответе, вы можете сопоставить любой объект со средством чтения данных (точнее: вводить значения из IDataReader в объект ЛЮБОГО ТИПА)   -  person Omu    schedule 15.06.2010
comment
Почему бы вам тогда не использовать выделенный ORM? Микро-ORM, вроде Dapper, кажется, здесь хорошо подходит.   -  person nawfal    schedule 29.07.2015
comment
@nawfal, этот вопрос был задан в июле 2009 года.   -  person user366312    schedule 29.07.2015
comment
@BROY Честно говоря, с комментариями, ответами и т. Д. Всегда учитываются и будущие посетители. И это не значит, что ORM не существовало в 2009 году :)   -  person nawfal    schedule 29.07.2015
comment
@nawfal, тогда почему бы тебе не предложить награду за обновление ответа?   -  person user366312    schedule 29.07.2015
comment
@BROY есть масса вопросов на одну и ту же тему, и я уверен, что на них есть ответы в другом месте. Один из таких: stackoverflow.com/questions/19841120/. Как я уже сказал, вы можете использовать микро-ORM. Даппер делает это.   -  person nawfal    schedule 29.07.2015
comment
@nawfal, это предлог для того, чтобы не предлагать награду, или доказательство того, что вопрос был бесполезным? Ссылка, которую вы показали, была задана в 2013 году.   -  person user366312    schedule 29.07.2015
comment
Связано: stackoverflow.com/questions/812034/   -  person nawfal    schedule 01.08.2015


Ответы (10)


Я использую 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);
            }
        }
    }
person Omu    schedule 15.06.2010
comment
Есть ли где-нибудь библиотека общих и полезных инъекций? - person Pavel Hodek; 14.12.2012
comment
@PavelHodek библиотеки нет, но их много в основном демонстрационном решении valueinjecter, а также на страницах codeplex. - person Omu; 14.12.2012
comment
Несколько вещей. 1) Вы используете отражение для установки значения, это плохо для производительности. Перейти маршрут выражения. 2) Где в вашей библиотеке находятся DataReaderInjection и KnownSourceValueInjection? Я думаю, вы изменили имена после этого ответа? 3) Использует ли отражение за кулисами каждый раз в цикле while reader.Read для получения имен свойств? Я надеюсь, что нет, но не могу убедить себя, не увидев класс KnownSourceValueInjection. - person nawfal; 29.07.2015
comment
4) Это незначительно, но все же - вы не назначаете свойство, когда оно равно null в базе данных (случай DBNull), но что, если этому свойству присваивается какое-то значение при создании в конструкторе, например var o = new User ()? В этом случае пользователь, которого вы возвращаете, будет отличаться от того, что на самом деле присутствует в БД. - person nawfal; 29.07.2015

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

public static IEnumerable<T> MapObject(IDataReader dr, Func<IDataReader, T> convertFunction)
        {
            while (dr.Read())
            {
                yield return convertFunction(dr);
            }
        }
person Jhonny D. Cano -Leftware-    schedule 09.07.2009
comment
+1 Интересное использование DI. Было бы неплохо, если бы тип T также обеспечивал реализацию convertFunction. :D - person Adrian Godong; 09.07.2009
comment
Это хороший DI, и его можно комбинировать с решением Omu. - person Pavel Hodek; 14.12.2012

Вы можете использовать этот класс LateBinder, который я написал: http://codecube.net/2008/12/new-latebinder/.

Я написал еще один пост с использованием: http://codecube.net/2008/12/using-the-latebinder/

person Joel Martinez    schedule 09.07.2009

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

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

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

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

person Nick Berardi    schedule 09.07.2009
comment
посмотри на мой ответ, это не так сложно :), и никаких атрибутов не требуется - person Omu; 18.08.2010

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

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

person Reed Copsey    schedule 09.07.2009

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

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

person ramnik    schedule 09.07.2009

как насчет того, чтобы следовать

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;

    }
}

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

person nWorx    schedule 09.07.2009
comment
Вы можете избежать условий if else и положиться на полиморфизм, если класс Book сам реализует некоторые Func‹IDataRecord, Book›. - person nawfal; 29.07.2015

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

person Giorgi    schedule 18.08.2010

Взгляните на http://CapriSoft.CodePlex.com.

person zackevans    schedule 08.12.2010

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

person Robin Orheden    schedule 08.12.2010