2010-06-16 46 views
45

Mientras la clase de mapeo estoy obteniendo el error 'T' debe ser un tipo no abstracto con un constructor público sin parámetros para usarlo como parámetro 'T' en el tipo o método genérico.Error de mapeo de clase: 'T' debe ser un tipo no abstracto con un constructor público sin parámetros

A continuación es mi SqlReaderBase Clase

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 
    } 

Lo que estoy tratando de hacer es, estoy executine algún comando y llenando mi clase de forma dinámica. La clase se da a continuación:

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

    } 
} 

Para el llenado mis propiedades de clase dinámicamente tengo otra clase llamada MapperBase clase con métodos siguientes:

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

    } 

Hay otra clase que hereda el SqlreaderBaseClass llama DefaultSearch. Código está por debajo

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

Pero cada vez que trataba de construir, estoy consiguiendo el error 'T' debe ser un tipo no abstracta con un constructor sin parámetros pública con el fin de utilizarlo como parámetro de 'T' en el tipo genérico o método Incluso aquí está el restaurante tiene un constructor público parameterless.

Respuesta

85

El problema es que usted está tratando de utilizar el T de SqlReaderBase como el argumento tipo para MapperBase - pero no tiene ninguna restricción a las que T.

trate de cambiar su declaración SqlReaderBase a esto:

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

Aquí hay un ejemplo más corto que demuestra el mismo tema:

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

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

La solución es cambiar Foo<T> 's declaración:

class Foo<T> where T : new() 

Entonces el compilador sabrá que el T de Foo es un argumento de tipo válido para Bar.

+0

Gracias funcionó. Pero una cosa más es que hay alguna forma de llamar a MapperBase en SqlReaderBase sí mismo sin crear Mapperbase abstracto protegido GetMapper(); método. –

+1

@Amit: Puede tener un parámetro de tipo * separado * 'TMapper' con una restricción:' donde TMapper: MapperBase , new() ' –

+0

por favor proporcione la declaración real ... sería una gr8 ayuda –

9

Las restricciones se deben aplicar a todos los tipos de la cadena; por lo tanto, se necesita:

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

Sin esto, no se puede satisfacer la restricción de T en:

protected abstract MapperBase<T> GetMapper(); 

o

MapperBase<T> mapper = GetMapper(); 

desde MapperBase<> es solamente se puede utilizar cuando tiene T: new()

+0

Chicos, tu solución funcionó. Pero aún hay un problema más, publicado en el comentario de Jon –

1

Tuve el mismo problema. Debería haber leído el mensaje antes de buscar en Google. Necesitaba agregar un constructor sin parámetros ...:-)

public MyClass() { 
    //stuff 
} 
Cuestiones relacionadas