Fill any ArrayList with results of an SQL statement - c#

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

Related

Fetch a value of a property of an object in ArrayList

I have initialized an ArrayList in C# for windows form application. I am adding new objects with few properties of each object in the ArrayList, such as:
ArrayList FormFields = new ArrayList();
CDatabaseField Db = new CDatabaseField();
Db.FieldName = FieldName; //FieldName is the input value fetched from the Windows Form
Db.PageNo = PageNo; //PageNo, Description, ButtonCommand are also fetched like FieldName
Db.Description = Description;
Db.ButtonCommand = ButtonCommand;
FormFields.Add(Db);
Now When I want to check only the FieldName of each object in the ArrayList (suppose there are many objects in the ArrayList). How can I do that??
I tried:
for(int i=0; i<FormFields.Count; i++)
{
FieldName = FormFields[i].FieldName;
}
But this is generating error (in the IDE). I am new to C# programming, so can someone help me with this??
Error: Error 21 'object' does not contain a definition for 'FieldName'
and no extension method 'FieldName' accepting a first argument of type
'object' could be found (are you missing a using directive or an
assembly reference?)
ArrayList holds objects. It's not generic and type safe.That's why you need to cast your object to access it's properties. Instead consider using generic collections like List<T>.
var FormFields = new List<CDatabaseField>();
CDatabaseField Db = new CDatabaseField();
...
FormFields.Add(Db);
Then you can see that all properties will be visible because now compiler knows the type of your elements and allows you to access members of your type in a type-safe manner.
Finally figured out the answer.
I tried to cast the objects for each object saved in the arraylist and finally could fetch the required field of each object:
for (int i = 0; i < FormFields.Count; i++)
{
CDatabaseField Db = (CDatabaseField)FormFields[i];
Label1.Text = Db.FieldName; //FieldName is the required property to fetch
}
As already pointed and based on this:
The Item returns an Object, so you may need to cast the returned value
to the original type in order to manipulate it. It is important to
note that ArrayList is not a strongly-typed collection. For a
strongly-typed alternative, see List<T>.
However as an another option you can use the foreach loop instead of for. When foreach runs, it tries to cast element of ArrayList to CDatabaseField and if an element is not convertible to the CDatabaseField you will get an InvalidCastException:
foreach (CDatabaseField item in FormFields)
{
FieldName = item.FieldName;
}
According to foreach documentation and C#6 syntax the above code is equivalent to this:
var enumerator = FormFields.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
CDatabaseField item = (CDatabaseField)enumerator.Current;
}
}
finally
{
var disposable = enumerator as IDisposable;
disposable?.Dispose();
}

EF and stored procedure returning dynamic result

I have a stored procedure with an input that defines the columns that are returned.
How can I iterate through the result?
I have tried solutions similar to this:
var selectColsParam = new System.Data.SqlClient.SqlParameter()
{
ParameterName = "#SelectCols",
Value = "Person.FirstName",
};
string sql = string.Format("dbo.DynamicResultSP {0} ", selectColsParam.ParameterName);
var result = db.Database.SqlQuery<List<dynamic>>(sql, selectColsParam);
At best, 'result' contains the correct number of rows which I can iterate through, but the 'row' itself is simply an object I can't seem to do anything with.
I don't need to know the column names but need to be able to iterate through the fields.
I know having a stored procedure that returns different columns depending on input is not considered good design, however, this is what I have to work with and so changing the SP is not an option.
Any help is appreciated
I ran into the same problem today, and had to resort back to SqlCommand rather than use the object context directly.
// Retrieve the connection from the object context
var entityConnection = this.ObjectContext.GetConnection() as EntityConnection;
var dbConnection = entityConnection.StoreConnection as SqlConnection;
// Create the command and associated parameters (dynamically passed in perhaps)
var command = new SqlCommand("dbo.DynamicResultSP");
command.Parameters.AddWithValue("#SelectCols", "Person.FirstName");
////command.Parameters.AddWithValue("#AnotherParameter", "Parameter.SecondValue");
////command.Parameters.AddWithValue("#AThirdParameter", "YetAnotherValue");
dbConnection.Open();
using (var reader = command.ExecuteReader())
{
// Get the column names
columnNames = new string[reader.FieldCount];
for (int i = 0; i < reader.FieldCount; i++)
{
columnNames[i] = reader.GetName(i);
}
// Get the actual results
while (reader.Read())
{
var result = new string[reader.FieldCount];
for (int i = 0; i < reader.FieldCount; i++)
{
result[i] = reader[i].ToString();
}
results.Add(result);
}
}
dbConnection.Close();
Now you should have access to both the names of the fields and the results.
Whilst it's not the prettiest solution, it gets the job done. Suggestions welcome.
I ran into a similar issue when testing JsonResults. You might be interested in a little extension I wrote that allows you to convert an object to a dynamic which would allow you to hook into the runtime dynamic binding on each object.
It might look like:
var result = db.Database.SqlQuery<List<object>>(sql, selectColsParam);
var dynamicResults = result.Select(o => o.AsDynamic()).ToList();
foreach (dynamic item in dynamicResults)
{
// treat as a particular type based on the position in the list
}
It's also possible that you might simply need to convert each element to the proper type based on whatever logic you have to determine that using Convert or by directly casting.

Unable to cast object of type 'Npgsql.Forward Only DataReader' to type 'Object'

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

List<> of objects, different types, sort and pull out types individually?

I've got a handful of products, any, all, or none of which may be associated with a specific submission. All 7 products are subclasses of the class Product. I need to store all the products associated with a submission, and then retrieve them and their field data on my presentation layer. I've been using a List<Product>, and List<object>, but when I use the OfType<EPL(specific subclass)>, I throw an error saying that I can't implicitly convert systems.generic.IEnumerable<EPL> to type 'Product'. I've tried to cast, but to no avail.
When I use prodlist.OfType<EPL>(); there are no errors, but when I try and store that in an instance of EPL "tempEpl", I get the aforementioned cast-related error. What gives? Code below.
ProductService pserv = new ProductService();
IList<object> prodlist = pserv.getProductById(x);
EPL tempEpl = new EPL();
if ((prodlist.OfType<EPL>()) != null)
{
tempEpl = prodlist.OfType<EPL>(); // this throws a conversion error.
}
the Data layer
List<object> TempProdList = new List<object>();
conn.Open();
SqlCommand EplCmd = new SqlCommand(EPLQuery, conn);
SqlDataReader EplRead = null;
EplRead = EplCmd.ExecuteReader();
EPL TempEpl = new EPL();
if (EplRead.Read())
{
TempEpl.Entity1 = EplRead.GetString(0);
TempEpl.Employees1 = EplRead.GetInt32(1);
TempEpl.CA1 = EplRead.GetInt32(2);
TempEpl.MI1 = EplRead.GetInt32(3);
TempEpl.NY1 = EplRead.GetInt32(4);
TempEpl.NJ1 = EplRead.GetInt32(5);
TempEpl.PrimEx1 = EplRead.GetInt32(6);
TempEpl.EplLim1 = EplRead.GetInt32(7);
TempEpl.EplSir1 = EplRead.GetInt32(8);
TempEpl.Premium1 = EplRead.GetInt32(9);
TempEpl.Wage1 = EplRead.GetInt32(10);
TempEpl.Sublim1 = EplRead.GetInt32(11);
TempProdList.Add(TempEpl);
}
This code makes no sense:
Product tempEpl = new EPL();
if ((prodlist.OfType<EPL>()) != null)
{
prodlist.OfType<EPL>();
}
It's unclear why you're creating a new EPL() to start with
OfType() will never return null - it returns a sequence, which may be empty
Calling OfType() won't do anything useful on its own, as per the body of your if statement
It's important to understand that OfType() returns a sequence, not a single item. I suspect that's what you were missing before.
I suspect you want:
Product tempEpl = prodList.OfType<EPL>().FirstOrDefault();
This will assign a value of null to tempEpl if there are no elements of type EPL in prodList, or the first EPL element in the list otherwise.
(It's not clear why you're returning a List<object> from the data layer to start with. Why not a List<Product>?)
I think in DAL instead of returning a list of Object type you should return a list of Product type. If you do so then there is no need for casting it to Product type again.
Second thing, in the PL, instead of using IList just use List.

Working with DataGridViewRow.DataBoundItem - transform to Enumerable Object?

I have a DataGridView with
myGridView.DataSource = GetSomeData()
// method called
public IQueryable GetSomeData()
{
var source = from r in records
select r;
return source; // made correction from 'r' to 'source'
}
GetSomeData() fills the DataGridView as expected. The user will select a row to edit which then passes the row data to a form. Since the DataGridViewRow.DataBoundItem is an anonymous type, how can I pass DataBoundItem?
I half expected that my DataBoundItem would be IQueryable - incorrect. Here is the info from the debugger on the DataBoundItem property:
DataBoundItem { CustomerID = "3133",
Last_Name = "Smith", First_Name =
"John", AccountNumber = "JS3133",
ActiveYN = True } < Anonymous Type >
Once the data is passed to the new form, I would like to do something like:
txtFName.Text = SomeEnumeratedObject["First_Name"];
txtLName.Text = SomeEnumeratedObject["Last_Name"];
Any ideas on how I can do this? It would be even better, IMO, if the controls on the new form could some how be bound to SomeEnumeratedObject.
Would it possible to query the DataBoundItem with LINQ?
Edit:
Changed method:
public DataView GetSomeData()
{
var source = from r in records
select r;
DataTable table = ToDataTable(myContext, source);
return new DataView(table);
}
See complete solution here.
You could pass it as dynamic and access the properties that way:
public void Test(dynamic item)
{
MessageBox.Show(string.Format("{0} : {1}", item.First_Name, item.Last_Name));
textBox1.DataBindings.Add("Text", _item, "First_Name");
textBox2.DataBindings.Add("Text", _item, "Last_Name");
}
One thing to consider is that properties on anonymous types are read only, so your user will not be able to edit the values that way. From the C# Language Spec:
The members of an anonymous type are a sequence of read-only properties inferred from the anonymous object initializer used to create an instance of the type.
You should consider declaring a type for this instead of using an anonymous type. That way you'll also get the benefit of intellisense and be able to rename/refactor your properties without any problems.
What type of objects are in records? You should be able to cast DataGridViewRow.DataBoundItem to whatever type of object records holds.
Say records is a list of Customer objects. You should be able to go:
txtFName.Text = ((Customer)DataGridViewRow.DataBoundItem).First_Name;
txtLName.Text = ((Customer)DataGridViewRow.DataBoundItem).Last_Name;
If that isn't possible, then I think you will have to use reflection:
Type type = DataGridViewRow.DataBoundItem.GetType();
String firstName = (String)type.GetProperty("First_Name")
.GetValue(DataGridViewRow.DataBoundItem, null);
Both kevev22 & adrift had the right idea: create a class to hold the data. Since I can transform an IQueryable resultset to a DataTable, it only made sense to do that:
public DataView GetSomeData()
{
var source = from r in records
select r;
DataTable table = ToDataTable(myContext, source);
return new DataView(table);
}
Now:
myDGView.DataSource = GetSomeData();
And, when a datagrid row is selected, you can cast the DataBoundItem to DataRow.
FYI - to transform an IQueryable resultset to a DataTable:
public DataTable ToDataTable(DataContext context, IQueryable source)
{
DataTable table = new DataTable();
{
adapter.SelectCommand = context.GetCommand(source);
sqlCommand.Connection.Open();
adapter.FillSchema(table, SchemaType.Source);
adapter.Fill(table);
}
return table;
}

Categories

Resources