I am getting a Parameter Count mismatch error which I don't understand.
I have the below code:
Type target = Type.GetType("CPS_Service." + DocumentType);
// Create an instance of my target class
instance = Activator.CreateInstance(target);
foreach (XElement pQ in PQData.Elements())
{
try
{
// populate the member in the instance of the data class with the value from the MQ String
if (target.GetProperty(pQ.Attribute("name").Value) != null)
{
target.GetProperty(pQ.Attribute("name").Value).SetValue(instance, pqRequest[Convert.ToInt32(pQ.Attribute("pos").Value)], null);
}
}
}
PropertyInfo[] properties = target.GetProperties();
foreach (PropertyInfo property in properties)
{
DataColumn col = new DataColumn(property.Name);
col.DataType = System.Type.GetType("System.String");
col.DefaultValue = "";
dt.Columns.Add(col);
}
DataRow dr = dt.NewRow();
foreach (PropertyInfo property in properties)
{
string value = property.GetValue(instance).ToString();
dr[property.Name.ToString()] = "";
}
dt.Rows.Add(dr);
return dt; //
so I am instantiating a generic class and populating it from a string array (taken from a tab-delimited string), then I need to either output a List or a datatable from the class instance
When populating the datarow dr for my datatable dt I am trying to get the value from the class:
string value = property.GetValue(instance, null).ToString();
dr[property.Name.ToString()] = "";
but on the line property.GetValue(instance).ToString(); I get the below error:
Parameter count mismatch
I have searched around and the other questions about this error do not apply...
Or would I be better off just casting my class to a List and returning that?
If you're trying to get the value of all properties of a String (or any type that has an indexer), then you're going to have to have a special case to deal with the indexer. So if you wanted to get the value of that parameter you'd have to pass the object array of values with one parameter as the index value you wanted to get.
For example, property.GetValue(test, new object [] { 0 }); would get the value of the string at index 0. So if the string's value was "ABC", the result of would be 'A'.
The easiest thing would be just to skip the indexer. You can test if a property is an indexer by using property.GetIndexParameters().Any(). I thought you could skip this check by using the appropriate binding flag when calling GetProperties(), but if you can, I didn't see it.
If you want to skip the index in your code, then change:
PropertyInfo[] properties = target.GetProperties();
To:
var properties = target.GetProperties().Where(p => !p.GetIndexParameters().Any());
Related
I'm trying to create a method that should get a generic class as parameter and return a datatable based on its fields.
The method I have so far is:
public DataTable TranformClassIntoDataTable<T>(T GenericClass)
{
DataTable dt = new DataTable();
Type objType = typeof(T);
FieldInfo[] info = objType.GetFields();
if (info.Length != 0)
{
for (int i = 0; i < info.Length; i++)
{
// PROBLEM HERE: the part inside of the typeof() isn't accepted by C#
dt.Columns.Add(info[i].Name, typeof(info[i].GetType());
}
}
else
{
throw new ArgumentException("No public fields are defined for the current Type");
}
return dt;
}
The error I get when I try to run it is the following:
Array size cannot be specified in a variable declaration
You should change this declaration
dt.Columns.Add(info[i].Name, typeof(info[i].GetType());
to the following
dt.Columns.Add(info[i].Name, info[i].FieldType);
Add method accepts string as column name and Type as column type, obviously. FieldType property contains the type of object which this field belongs to
I have found below code on stackoverflow. But i am not getting what fundamentally this code is doing. Can anyone please explain me how this code works?
public static List<T> ToListof<T>(DataTable dt)
{
const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
var columnNames = dt.Columns.Cast<DataColumn>()
.Select(c => c.ColumnName)
.ToList();
var objectProperties = typeof(T).GetProperties(flags);
var targetList = dt.AsEnumerable().Select(dataRow =>
{
var instanceOfT = Activator.CreateInstance<T>();
foreach (var properties in objectProperties.Where(properties => columnNames.Contains(properties.Name) && dataRow[properties.Name] != DBNull.Value))
{
properties.SetValue(instanceOfT, dataRow[properties.Name], null);
}
return instanceOfT;
}).ToList();
return targetList;
}
Specially i want to know at where coloumn's data is getting type casted. I have searched on many links but i am not getting proper answer anywhere.
It attempts to convert a datatable to a list of objects of type T, dynamically at runtime.
var objectProperties = typeof(T).GetProperties(flags);
This line uses Reflection to get a list of public properties on type T.
var targetList = dt.AsEnumerable().Select(dataRow =>
This line iterates the DataTable as an IEnumerable, getting an instance called dataRow for each row.
var instanceOfT = Activator.CreateInstance<T>();
This creates a new instance of type T using reflection, inside the loop. This means a new T is created for each dataRow.
foreach (var properties in objectProperties.Where(properties =>
columnNames.Contains(properties.Name)
This goes over all the properties of T we got back in the beginning, which are also in columnNames - meaning that there's a column with value for them
&& dataRow[properties.Name] != DBNull.Value))
The second half of the condition makes sure that the column has a value and isn't NULL.
properties.SetValue(instanceOfT, dataRow[properties.Name], null);
This uses reflection, again to set the value from the datarow into the property of T.
).ToList();
This takes all the items returned from the Select statement and returns a List from them.
The code isn't the neatest, but the variables are pretty well-named and clear, if you know how reflection works. As for your second question - there's no casting, because this code assumes that the type of the value in the DataRow matches the type of the property. If it doesn't, an exception will be thrown.
In Detail:
const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
this will combine the public and the instance flag so that only Public non static methods will be searched.
var columnNames = dt.Columns.Cast<DataColumn>()
.Select(c => c.ColumnName)
.ToList();
this will list all column names from the data table
var objectProperties = typeof(T).GetProperties(flags);
gets the Type of the generic argument and will list all public, non static properties
dt.AsEnumerable().Select
creates an IEnumerable of each data row in the DataTable
var instanceOfT = Activator.CreateInstance<T>();
this creates a new instance as you would use new
foreach (var properties in objectProperties.Where(properties => columnNames.Contains(properties.Name) && dataRow[properties.Name] != DBNull.Value))
{
properties.SetValue(instanceOfT, dataRow[properties.Name], null);
}
this will iterate through all propertys of T whos also contained in the datatable and not null (eg. DbNull from the database)
then it calls SetValue. As the dataRow will already return the value as its stored in the database there is no cast nessesary. This does only work if the Property and the type in the database are "the same". As NVarchar for string.
I am getting following error when trying to insert data into list.
"Unable to cast object of type 'Npgsql.Forward Only DataReader' to type 'object'"
here is my code.RDLCReportBase is my base class used to inherit in another class.
public List<RDLCReportBase> GetList(RDLCReportBase reportbase,string )
{
List<RDLCReportBase> list = new List<RDLCReportBase>();
Database.AddInParameter(reportbase.command, "culturecode", NpgsqlDbType.Varchar, cultureCode);
using (IDataReader rdr = Database.ExecuteReader(reportbase.command))
{
while (rdr.Read())
{
list.Add((RDLCReportBase)rdr);
}
}
return list;
}
anybody knows how to solve this issue ?
Thanks
I think your problem in this line:
list.Add((RDLCReportBase)rdr);
You can't cast IDataReader directly to your classes. Instead IDataReader helps you to access columns of the current row through the indexers and methods so you may process all received data row by row. To go to the next row you use rdr.Read(). So your code should looks like this (of course, you must replace properties and column names with your own):
using (IDataReader rdr = Database.ExecuteReader(reportbase.command))
{
while (rdr.Read())
{
var value = new RDLCReportBase
{
Property1 = rdr.GetInt32(0), // access zero column value wich is type of int
Property2 = (string)rdr["Column2"], // access column value with name Column2 wich is type of string
// and so on...
};
list.Add(value);
}
}
I have the following code that takes an SQL statement (string), loads the results into an ArrayList (organisationList) which is a collection of Organisations:
public void FillDataGridView(DataGridView grid, string SQLCommand)
{
SqlCommand dataCommand = new SqlCommand();
dataCommand.Connection = dataConnection;
dataCommand.CommandType = CommandType.Text;
dataCommand.CommandText = SQLCommand;
SqlDataReader dataReader = dataCommand.ExecuteReader();
while (dataReader.Read())
{
Organisation org = new Organisation();
org.OrganisationId = (int)dataReader["OrganisationId"];
org.OrganisationName = (string)dataReader["OrganisationName"];
organisationList.Add(org);
}
grid.DataSource = organisationList;
dataReader.Close();
}
I would like to adapt this method to be possible to fill an ArrayList passed into it.
Is it possible for me to pass the list into the method and have something like:
public void FillArrayList(DataGridView grid, SqlDataReader reader, ArrayList list)
{
//Fill the list with the contents of the reader
while (reader.Read())
{
Object obj = new Object
for(int i; i = 0; i < obj.NoOfProperties)
{
obj.Property[i] = reader[i];
}
list.Add(obj);
}
}
Sorry if this is a little vague, I'm quite new to OOP and a bit lost!
Edit: Based on the advice of Darren Davies, I have modified the method as follows:
public void FillArrayList<T>(DataGridView grid, SqlDataReader reader, List<T> list)
{
//Fill the list with the contents of the reader
while (reader.Read())
{
Object obj = new Object();
Type type = typeof(T);
FieldInfo[] fields = type.GetFields(); // Get the fields of the assembly
int i = 0;
foreach(var field in fields)
{
field.SetValue(obj, reader[i]); // set the fields of T to the reader's value
// field.setValue(obj, reader[field.Name]); // You can also set the field value to the explicit reader name, i.e. reader["YourProperty"]
i++;
}
list.Add((T)obj);
}
grid.DataSource = list;
}
When I run the code, I get an error when casting the object to type T:
Unable to cast object of type 'System.Object' to type
'TestHarness.Organisation'.
I was under the impression that an Object could store anything. Can anyone advise me on why this cast cannot be performed?
Thanks,
Andy
Unless you are using .NET 1.1, you probably shouldn't be using ArrayList; the generic List<T> is preferable.
You cannot add members to object - it is not extensible. You would need to know the type of object to create. Generics would be a reasonable object. However, to save you some time, you might do well to look at dapper:
var list = dataConnection.Query<YourType>(SQLCommand).ToList();
Which will do everything, using a direct column-name to member-name mapping. You would need to create a YourType class with the properties (appropriately typed) that you expect.
If you are using 4.0, dapper also supports dynamic:
var list = dataConnection.Query(SQLCommand).ToList();
This uses dynamic, so you can still do (without declaring a type):
foreach(var obj in list) {
Console.WriteLine(obj.OrganisationId);
Console.WriteLine(obj.OrganisationName);
}
Personally I'd only use the dynamic approach if the data is used very close to where it is accessed. For returning from a method, the generic approach is preferred. Likewise, dynamic doesn't work well with DataGridView.
Finally, I notice no parameters; you always want to use parameters and not concatenation. Dapper supports that too:
string foo = ...;
var list = dataConnection.Query<YourType>(
"select * from SomeTable where Foo = #foo", new { foo }).ToList();
If you are using C# 2.0. or greater use Generics rather than ArrayList.
You could use Reflection to the get the properties of the type you pass in:
public void FillArrayList<T>(DataGridView grid, SqlDataReader reader, List<T> list)
{
//Fill the list with the contents of the reader
while (reader.Read())
{
Object obj = new Object();
Type type = typeof(T); // get the type of T (The paramter you passed in, i.e. Organisations)
FieldInfo[] fields = type.GetFields(); // Get the fields of the assembly
int i = 0;
foreach(var field in fields) // Loop round the fields
{
field.setValue(obj, reader[i]); // set the fields of T to the readers value
// field.setValue(obj, reader[field.Name]); // You can also set the field value to the explicit reader name, i.e. reader["YourProperty"]
i++;
}
list.Add(obj);
}
}
To invoke it:
FillArrayList(grid, reader, list);
Where list is a List type of Organisations
List<Organisations> list = new List<Organisations>();
http://msdn.microsoft.com/en-us/library/ms379564(v=vs.80).aspx
If you create the properties of your class with same name as the columns in the returned data set then you can use reflection to construct any type of object from data reader or data table (if columns of data reader or data table and properties of object are matched).
You can see followin this link. It shows how to convert data table to collection of custom class. Working with data reader would be same.
Couldn't you also use DataContext and give your connection string and then use Linq?
http://msdn.microsoft.com/en-us/library/bb399375.aspx
I am using a PivotGrid(DevExpress). I want to set AppearancePrint property settings in a for loop.
How do i use the variable type for properties such as Cell in the example below?
so instead of
grid.AppearancePrint.Cell.BackColor = Color.White;
grid.AppearancePrint.Cell.BackColor2 = Color.LightBlue;
I want to do this:
//datarow example <PrintAppearance Type="Cell" Font="Tahoma,8,Regular" BackColor="White" BackColor2="Light Grey"/>
foreach (DataRow dr in appearances)
{
string type = dr["Type"].ToString();
grid.AppearancePrint.[type].BackColor = Color.FromName(dr["BackColor"].ToString());
grid.AppearancePrint.[type].BackColor2 = Color.FromName(dr["BackColor2"].ToString());
}
This is essentially a form of script-parsing, and you'll need to use reflection in order to do it. For example:
foreach (DataRow dr in appearances) {
string type = dr["Type"].ToString();
PropertyInfo propertyForType = grid.AppearancePrint.GetType().GetProperty(type);
object objectForProperty = propertyForType.GetValue(grid.AppearancePrint, null);
PropertyInfo propertyForBackColor = objectForProperty.GetType().GetProperty("BackColor");
PropertyInfo propertyForBackColor2 = objectForProperty.GetType().GetProperty("BackColor2");
propertyForBackColor.SetValue(objectForProperty, Color.FromName(dr["BackColor"].ToString()), null);
propertyForBackColor2.SetValue(objectForProperty, Color.FromName(dr["BackColor2"].ToString()), null);
}
I'm not familiar with your exact problem but at a glance, it seems you'll need to use reflection as you won't know the type until runtime - In case you're not familiar with reflection, it will allow you to examine the object (and more importantly the properties on it)
See here for a possible solution