web-dev-qa-db-fra.com

Erreur de mappage de classe: «T» doit être un type non abstrait avec un constructeur public sans paramètre

Alors que la classe de mappage j'obtiens l'erreur "T" doit être un type non abstrait avec un constructeur public sans paramètre afin de l'utiliser comme paramètre "T" dans le type ou la méthode générique.

Ci-dessous ma classe 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's 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
    }

Ce que j'essaie de faire, c'est d'exécuter une commande et de remplir ma classe dynamiquement. La classe est donnée ci-dessous:

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() { }

    }
}

Pour remplir dynamiquement mes propriétés de classe, j'ai une autre classe appelée MapperBase Class avec les méthodes suivantes:

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

    }

Il existe une autre classe qui hérite de SqlreaderBaseClass appelée DefaultSearch. Le code est ci-dessous

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

Mais chaque fois que j'essaye de construire, j'obtiens l'erreur 'T' doit être un type non abstrait avec un constructeur public sans paramètre afin de l'utiliser comme paramètre 'T' dans le type ou la méthode générique. Même ici, le restaurant a un constructeur public sans paramètre.

64
user240141

Le problème est que vous essayez d'utiliser le T de SqlReaderBase comme argument de type pour MapperBase - mais vous n'avez aucune contrainte sur queT.

Essayez de changer votre déclaration SqlReaderBase en ceci:

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

Voici un exemple plus court qui illustre le même problème:

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

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

Le correctif consiste à modifier Foo<T> Déclaration de:

class Foo<T> where T : new()

Le compilateur saura alors que le T de Foo est un argument de type valide pour Bar.

114
Jon Skeet

Les contraintes doivent s'appliquer à chaque type de la chaîne; vous avez donc besoin de:

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

Sans cela, vous ne pouvez pas satisfaire la contrainte pour T dans:

protected abstract MapperBase<T> GetMapper();

ou

MapperBase<T> mapper = GetMapper();

puisque MapperBase<> est niquement utilisable lorsque T a : new()

12
Marc Gravell

J'ai eu le même problème. J'aurais dû lire le message avant de le googler. J'avais besoin d'ajouter un constructeur sans paramètre ... :-)

public MyClass() {
  //stuff
}
3
Yohan Dahmani