Ошибка сопоставления классов: "T" должен быть не абстрактным типом с открытым конструктором без параметров

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

Ниже мой класс SqlReaderBase

 public abstract class SqlReaderBase<T> : ConnectionProvider
    {

        #region  Abstract Methods

        protected abstract string commandText { get; }
        protected abstract CommandType commandType { get; }
        protected abstract Collection<IDataParameter> GetParameters(IDbCommand command);
        **protected abstract MapperBase<T> GetMapper();**

        #endregion

        #region Non Abstract Methods

        /// <summary>
        /// Method to Execute Select Queries for Retrieveing List of Result
        /// </summary>
        /// <returns></returns>
        public Collection<T> ExecuteReader()
        {
            //Collection of Type on which Template is applied
            Collection<T> collection = new Collection<T>();

            // initializing connection
            using (IDbConnection connection = GetConnection())
            {
                try
                {
                    // creates command for sql operations
                    IDbCommand command = connection.CreateCommand();

                    // assign connection to command
                    command.Connection = connection;

                    // assign query
                    command.CommandText = commandText;

                    //state what type of query is used, text, table or Sp 
                    command.CommandType = commandType;

                    // retrieves parameter from IDataParameter Collection and assigns it to command object
                    foreach (IDataParameter param in GetParameters(command))
                        command.Parameters.Add(param);


                    // Establishes connection with database server
                    connection.Open();

                    // Since it is designed for executing Select statements that will return a list of results 
                    // so we will call command execute  reader method that return a Forward Only reader with 
                    // list of results inside. 
                    using (IDataReader reader = command.ExecuteReader())
                    {
                        try
                        {
                            // Call to Mapper Class of the template to map the data to its 
                            // respective fields
                            MapperBase<T> mapper = GetMapper();
                            collection = mapper.MapAll(reader);

                        }
                        catch (Exception ex)            // catch exception
                        {
                            throw ex;     // log errr
                        }
                        finally
                        {
                            reader.Close();
                            reader.Dispose();
                        }
                    }
                }
                catch (Exception ex)
                {
                    throw ex;
                }
                finally
                {
                    connection.Close();
                    connection.Dispose();
                }
            }
            return collection;
        }
        #endregion
    }

То, что я пытаюсь сделать, я выполняю какую-то команду и динамически заполняю свой класс. Класс приведен ниже:

namespace FooZo.Core
{
    public class Restaurant
    {


        #region Private Member Variables
        private int _restaurantId = 0;
        private string _email = string.Empty;
        private string _website = string.Empty;
        private string _name = string.Empty;
        private string _address = string.Empty;
        private string _phone = string.Empty;
        private bool _hasMenu = false;
        private string _menuImagePath = string.Empty;
        private int _cuisine = 0;
        private bool _hasBar = false;
        private bool _hasHomeDelivery = false;
        private bool _hasDineIn = false;
        private int _type = 0;
        private string _restaurantImagePath = string.Empty;
        private string _serviceAvailableTill = string.Empty;
        private string _serviceAvailableFrom = string.Empty;

        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        public string Address
        {
            get { return _address; }
            set { _address = value; }
        }
        public int RestaurantId
        {
            get { return _restaurantId; }
            set { _restaurantId = value; }
        }
        public string Website
        {
            get { return _website; }
            set { _website = value; }
        }

        public string Email
        {
            get { return _email; }
            set { _email = value; }
        }
        public string Phone
        {
            get { return _phone; }
            set { _phone = value; }
        }

        public bool HasMenu
        {
            get { return _hasMenu; }
            set { _hasMenu = value; }
        }

        public string MenuImagePath
        {
            get { return _menuImagePath; }
            set { _menuImagePath = value; }
        }

        public string RestaurantImagePath
        {
            get { return _restaurantImagePath; }
            set { _restaurantImagePath = value; }
        }

        public int Type
        {
            get { return _type; }
            set { _type = value; }
        }

        public int Cuisine
        {
            get { return _cuisine; }
            set { _cuisine = value; }
        }

        public bool HasBar
        {
            get { return _hasBar; }
            set { _hasBar = value; }
        }

        public bool HasHomeDelivery
        {
            get { return _hasHomeDelivery; }
            set { _hasHomeDelivery = value; }
        }

        public bool HasDineIn
        {
            get { return _hasDineIn; }
            set { _hasDineIn = value; }
        }

        public string ServiceAvailableFrom
        {
            get { return _serviceAvailableFrom; }
            set { _serviceAvailableFrom = value; }
        }

        public string ServiceAvailableTill
        {
            get { return _serviceAvailableTill; }
            set { _serviceAvailableTill = value; }
        }


        #endregion

        public Restaurant() { }

    }
}

Для динамического заполнения свойств класса у меня есть другой класс, называемый классом MapperBase со следующими методами:

 public abstract class MapperBase<T> where T : new()
    {
        protected T Map(IDataRecord record)
        {
           T  instance = new T();

            string fieldName;
            PropertyInfo[] properties = typeof(T).GetProperties();

            for (int i = 0; i < record.FieldCount; i++)
            {
                fieldName = record.GetName(i);

                foreach (PropertyInfo property in properties)
                {
                    if (property.Name == fieldName)
                    {
                        property.SetValue(instance, record[i], null);
                    }
                }
            }

            return instance;
        }
        public Collection<T> MapAll(IDataReader reader)
        {
            Collection<T> collection = new Collection<T>();

            while (reader.Read())
            {

                    collection.Add(Map(reader));

            }

            return collection;
        }

    }

Существует еще один класс, который наследует SqlreaderBaseClass, называемый DefaultSearch. Код ниже

 public class DefaultSearch: SqlReaderBase<Restaurant>
{
    protected override string commandText
    {
        get { return "Select Name from vw_Restaurants"; }
    }

    protected override CommandType commandType
    {
        get { return CommandType.Text; }
    }

    protected override Collection<IDataParameter> GetParameters(IDbCommand command)
    {
        Collection<IDataParameter> parameters = new Collection<IDataParameter>();
        parameters.Clear();
        return parameters;
    }



    protected override MapperBase<Restaurant> GetMapper()
    {
        MapperBase<Restaurant> mapper = new RMapper();
        return mapper;
    }
}

Но всякий раз, когда я пытался построить, я получаю сообщение об ошибке "Т" должен быть не абстрактным типом с открытым конструктором без параметров, чтобы использовать его в качестве параметра "Т" в родовом типе или методе. Даже здесь T имеет ресторан с конструктором Constructless Public.

Ответ 1

Проблема заключается в том, что вы пытаетесь использовать T from SqlReaderBase как аргумент типа для MapperBase - но у вас нет ограничений на T.

Попробуйте изменить объявление SqlReaderBase на следующее:

public abstract class SqlReaderBase<T> : ConnectionProvider
    where T : new()

Вот более короткий пример, демонстрирующий ту же проблему:

class Foo<T>
{
    Bar<T> bar;
}

class Bar<T> where T : new()
{
}

Исправление заключается в изменении объявления Foo<T> на:

class Foo<T> where T : new()

Тогда компилятор будет знать, что T из Foo является допустимым аргументом типа для Bar.

Ответ 2

Ограничения должны применяться к каждому типу в цепочке; поэтому вам нужно:

public abstract class SqlReaderBase<T> : ConnectionProvider where T : new()

Без этого вы не можете удовлетворить ограничение для T в:

protected abstract MapperBase<T> GetMapper();

или

MapperBase<T> mapper = GetMapper();

поскольку MapperBase<> можно использовать только тогда, когда T имеет : new()

Ответ 3

У меня была такая же проблема. Я должен был прочитать сообщение перед тем, как его погубить. Мне нужно было добавить конструктор без параметров...:-)

public MyClass() {
  //stuff
}