C# - IDataReader to Object mapping using generics

只谈情不闲聊 提交于 2019-12-18 04:06:13

问题


How can I map a DataReader object into a class object by using generics?

For example I need to do the following:

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;
        }
    }

And later I need to call this class-method like the following:

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);
}

Note that, the Mapper - class should be able to map object of any type represented by T.


回答1:


I use ValueInjecter for this

I'm doing like this:

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

you gonna need this ValueInjection for this to work:

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:


Well, i don't know if it fits here, but you could be using the yield keyword

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



回答3:


You could use this LateBinder class I wrote: http://codecube.net/2008/12/new-latebinder/.

I wrote another post with usage: http://codecube.net/2008/12/using-the-latebinder/




回答4:


This is going to be very hard to do for the reason that you are basically trying to map two unknowns together. In your generic object the type is unknown, and in your datareader the table is unknown.

So what I would suggest is you create some kind of column attribute to attach to the properties of you entity. And then look through those property attributes and try to look up the data from those attributes in the datareader.

Your biggest problem is going to be, what happens if one of the properties isn't found in the reader, or vice-versa, one of the columns in the reader isn't found in the entity.

Good luck, but if you want to do something like this, you probably want a ORM or at the very least some kind of Active Record implementation.




回答5:


The easiest way I can think of offhand would be to supply a Func<T,T> delegate for converting each column and constructing your book.

Alternatively, if you followed some conventions, you could potentially handle this via reflection. For example, if each column mapped to a property in the resulting object using the same name, and you restricted T in your Mapper to providing a constructable T, you could use reflection to set the value of each property to the value in the corresponding column.




回答6:


I don't think you'll be able to get around defining the relationship between fields in some form. Take a look at this article and pay particular attention to how the mapping is defined, it may work for you.

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




回答7:


what about following

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;

    }
}

Don't know if it is syntactically correct, but I hope u get the idea.




回答8:


What about using Fluent Ado.net ?




回答9:


Have a look at http://CapriSoft.CodePlex.com




回答10:


I would recommend that you'd use AutoMapper for this.



来源:https://stackoverflow.com/questions/1105549/c-sharp-idatareader-to-object-mapping-using-generics

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!