skip to Main Content

I am creating a generic data reader function for get data from database but i am facing a issue. "Unable to cast object of type ‘System.String’ to type ‘MyBooks.Data.Model.Book"

Kindly Help me

 public List<T> GetDataMethod<T>(SqlCommand cmd)
    {
        cmd.Connection = getcon();
        SqlDataReader reader = cmd.ExecuteReader();
        List<T> records = new List<T>();
        while (reader.Read())
        {
            object[] tempRow = new object[reader.FieldCount];
            for (int i = 0; i < reader.FieldCount; i++)
            {
                tempRow[i] = reader[i];
            }
            //Error showing > can not convert from object[] to T
            records.Add(tempRow);
        }
        return records;
    }

3

Answers


  1. You can use a generic type. You’ll need to return a list of objects then create code to map between the list of objects to the type you need.

    public List<object[]> GetDataMethod(SqlCommand cmd)
        {
            cmd.Connection = getcon();
            SqlDataReader reader = cmd.ExecuteReader();
            List<object[]> records = new List<object[]>();
            while (reader.Read())
            {
                object[] tempRow = new object[reader.FieldCount];
                for (int i = 0; i < reader.FieldCount; i++)
                {
                    tempRow[i] = reader[i];
                }
                //Error showing > can not convert from object[] to T
                records.Add(tempRow);
            }
            return records;
        }
    
    
    Login or Signup to reply.
  2. tempRow is a DataRow. And T is of type MyBooks.Data.Model.Book.
    What you want to do is map the values to the properties of Book.
    E.g. if Book has a property of "Title" and you know, that the title is in the DataRow at index 0:

    var newBook = new Book();
    newBook.Title = tempRow[0];
    records.Add(newBook);
    
    Login or Signup to reply.
  3. this is what all ORMs do. they need a specific mapper for each type that would be provided by the reflection, annotation, or anything else. Bytheway you could register your own mappers and use them inside your method.

    namespace Models {
        public record Book(int Id, string Name);
    }
    

    if you have model for Book.
    and you need an interface for all your mappers like this :

    namespace Mappers {
        public interface IMappers<T> {
            Task<T> MapAsync(object[] fieldsData);
        }
    }
    

    and the book mapper :

    using Models;
    
    namespace Mappers {
        public class BookMapper : IMappers<Book>
        {
            public Task<Book> MapAsync(object[] fieldsData)
            {
                return Task.Run(() => new Book( Convert.ToInt32( fieldsData[0]), fieldsData[1].ToString()));
            }
        }
    }
    

    also, you need somewhere to register your mappers to DI :

    using Mappers;
    using Microsoft.Extensions.DependencyInjection;
    using Models;
    
    namespace MiniOrm {
        public static class DependencyRegistration
        {
            public static IServiceCollection RegisterMappers(this IServiceCollection services)
            {
                services.AddTransient<IMappers<Book>,BookMapper>();
                return services;
            }
        }
    }
    

    now you would be able to change your method like this :

    using System.Data.SqlClient;
    using Mappers;
    using Microsoft.Extensions.DependencyInjection;
    
    namespace MiniOrm {
        public class CommandExecuter {
            private readonly IServiceProvider _serviceProvider;
            public CommandExecuter(IServiceProvider serviceProvider)
            {
                _serviceProvider = serviceProvider;
            }
    
            public List<T> GetDataMethod<T>(SqlCommand cmd)
            {
                cmd.Connection = getcon();
                SqlDataReader reader = cmd.ExecuteReader();
                List<T> records = new List<T>();
                while (reader.Read())
                {
                    object[] tempRow = new object[reader.FieldCount];
                    for (int i = 0; i < reader.FieldCount; i++)
                    {
                        tempRow[i] = reader[i];
                    }
                
                    var mapper = _serviceProvider.GetRequiredService<IMappers<T>>();
                    if(mapper == null)
                        throw new NotSupportedException($"No mapper found for type {typeof(T)}");
                    records.Add(mapper.MapAsync(tempRow).Result);
                }
                return records;
            }
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search