简体   繁体   中英

Cannot resolve a symbol for use as a generic type parameter

I am trying to create a new instance of a class from the Type which is found via reflection. When calling TableInsert() 'destType' cannot be resolved and when used as a parameter for the method, I get this error: "Argument type 'System.Type' is not assignable to parameter type 'destType.'" I have a feeling the answer to this is extremely simple and I've just been staring at the code far too long.

//this is executed first
private void ImportData(IDataMapping tableMap)
{
    //destType is a class of POCOs that match column names from a database table
    //the class inherits from a base class called SchemaObject which inherits from ISchemaObject which itself inherits from INotifyPropertyChanged
    //an example name would be 'SchemaEmployees' where employees is the destination table name
    Type destType = typeof(ISchemaObject).Assembly.GetTypes()
                    .FirstOrDefault(t => t.Name.ToLower() == "schema" + tableMap.tableName.ToLower());

    if (destType == null)
        throw new NullReferenceException("Could not find class mapping for table " + tableMap.tableName + ".");

    //the list of public properties on the POCO class, i.e. the columns from the database table
    List<PropertyInfo> destProps = destType.GetProperties().ToList();

    //here I create a DataSet from a DataAdapter for the destination insert table 
    //here I get a DbDataReader from a source database and run ExecuteReader()
    using (DataSet ds = GetTableDs())
    using (DbDataReader reader = GetSourceData())
        while (reader.Read())
        {
            TableInsert<destType>(tableMap, reader, ds, destType, destProps);
        }
}  

//this is where the DataRow for the destination database is created and the columns mapped to the class POCOs
private void TableInsert<T>(
    IDataMapping tableMap,
    DbDataReader r, //source
    DataSet ds, //destination
    T destClassType,
    IList<PropertyInfo> destClassProperties) 
    where T : ISchemaObject
{
    var dr = ds.Tables[0].NewRow();

    var dest = dr.CreateItemFromRow<destClassType>(destClassProperties) as ISchemaObject;

    //sets the DataRow column value when the class property value changes
    dest.PropertyChanged += (sender, eArgs) =>
    {
        dr[eArgs.PropertyName] =
            destClassType
            .GetType()
            .GetProperty(eArgs.PropertyName, BindingFlags.Public | BindingFlags.Instance)
            .GetValue(sender, null);
    };

    //the source DataReader row is converted to a Dynamic class
    //it's not type safe, but the source changes frequently so properly mapping it would add undue complexity to the project        
    var srcEnt = new DynamicEntityGenerator.DataReaderEntity(r);

    dynamic dynSrc = srcEnt;

    //in this method the destination class (bound to the DataRow that will be inserted) properties are mapped to the source Dynamic class's properties
    tableMap.MapInsertClasses<ISchemaObject>(dynSrc, dest);

    ds.Tables[0].Rows.Add(dr);
}  

//DataRow extension method for mapping the row to a class containing properties with the same name as the DataColumn names
public static T CreateItemFromRow<T>(
    this DataRow row,
    IList<PropertyInfo> properties)
    where T : new()
{
    T item = new T();

    foreach (var property in properties)
    {
        if (row[property.Name] != DBNull.Value)
            property.SetValue(item, row[property.Name], null);
    }

    return item;
}  

You cannot use generics in that way, but you actually don't need it. You can do:

//this is executed first
private void ImportData(IDataMapping tableMap)
{
    //destType is a class of POCOs that match column names from a database table
    //the class inherits from a base class called SchemaObject which inherits from ISchemaObject which itself inherits from INotifyPropertyChanged
    //an example name would be 'SchemaEmployees' where employees is the destination table name
    Type destType = typeof(ISchemaObject).Assembly.GetTypes()
                    .FirstOrDefault(t => t.Name.ToLower() == "schema" + tableMap.tableName.ToLower());

    if (destType == null)
        throw new NullReferenceException("Could not find class mapping for table " + tableMap.tableName + ".");

    //the list of public properties on the POCO class, i.e. the columns from the database table
    List<PropertyInfo> destProps = destType.GetProperties().ToList();

    //here I create a DataSet from a DataAdapter for the destination insert table 
    //here I get a DbDataReader from a source database and run ExecuteReader()
    using (DataSet ds = GetTableDs())
    using (DbDataReader reader = GetSourceData())
        while (reader.Read())
        {
            TableInsert(tableMap, reader, ds, destType, destProps);
        }
}  

//this is where the DataRow for the destination database is created and the columns mapped to the class POCOs
private void TableInsert(
    IDataMapping tableMap,
    DbDataReader r, //source
    DataSet ds, //destination
    Type destClassType,
    IList<PropertyInfo> destClassProperties)
{
    var dr = ds.Tables[0].NewRow();

    var dest = dr.CreateItemFromRow(destClassType, destClassProperties) as ISchemaObject;

    //sets the DataRow column value when the class property value changes
    dest.PropertyChanged += (sender, eArgs) =>
    {
        dr[eArgs.PropertyName] =
            destClassType
            .GetType()
            .GetProperty(eArgs.PropertyName, BindingFlags.Public | BindingFlags.Instance)
            .GetValue(sender, null);
    };

    //the source DataReader row is converted to a Dynamic class
    //it's not type safe, but the source changes frequently so properly mapping it would add undue complexity to the project        
    var srcEnt = new DynamicEntityGenerator.DataReaderEntity(r);

    dynamic dynSrc = srcEnt;

    //in this method the destination class (bound to the DataRow that will be inserted) properties are mapped to the source Dynamic class's properties
    tableMap.MapInsertClasses<ISchemaObject>(dynSrc, dest);

    ds.Tables[0].Rows.Add(dr);
}  

//DataRow extension method for mapping the row to a class containing properties with the same name as the DataColumn names
public static ISchemaObject CreateItemFromRow(
    this DataRow row,
    Type type,
    IList<PropertyInfo> properties)
{
    var item = (ISchemaObject)Activator.CreateInstance(type);

    foreach (var property in properties)
    {
        if (row[property.Name] != DBNull.Value)
            property.SetValue(item, row[property.Name], null);
    }

    return item;
}  

You can also validate that the type is actually a ISchemaObject and has a parameterless constructor if you need to.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM