I used myGeneration in some projects; I noticed that BusinessEntity keeps the result of the query in a DataTable,
But what if the developer wants to get the result as List of objects?
To solve this, I made the following modification for my own use.
public class BusinessEntity
{
//....
public DataTable TheDataTable
{
get
{
return _dataTable;
}
set
{
_dataTable = value;
}
}
public void PutTheRow(DataRow pRow)
{
DataRow newRow = _dataTable.NewRow();
newRow.ItemArray = pRow.ItemArray;
_dataTable.Rows.Add(newRow);
_dataRow = newRow;
//Rewind();
_dataTable.AcceptChanges();
}
.......
}
Now suppose we have a table "Employee" and we generate a class for him, also I make the following modification:
public abstract class Employee : _Employee
{
public Employee()
{
}
public List<Employee> AsList()
{
List<Employee> list = new List<Employee>();
Employee businessEntity = null;
foreach (System.Data.DataRow row in TheDataTable.Rows)
{
businessEntity = new Employee();
businessEntity.TheDataTable = TheDataTable.Clone();
businessEntity.PutTheRow(row);
list.Add(businessEntity);
}
return list;
}
}
Now I can Get a list of objects, from the result of the query, instead of one object and the result in the data table :
Employee employee = new Employee();
employee.Where.ID.Operator = WhereParameter.Operand.NotIn;
employee.Where.ID.Value = "1,2,3";
employee.Query.Load();
foreach (Employee employeeLoop in employee.AsList())
{
TreeNode node = new TreeNode(employeeLoop.s_ID);
node.Tag = employeeLoop;
mMainTree.Nodes.Add(node);
}
Then I can access the selected Employee as following:
Employee emp = (Employee) mMainTree.SelectedNode.Tag;
emp.Name = "WWWWW";
emp.Save();
thanks.
Do You have a better tips, Ideas?
for more discussion, please visit MyGeneration forum.
Why would you create a method called AsList() that only ever returns one Item? You could just create a generic extension method like this (created from the top of my head..):
public static List<T> AsList(this T item)
{
return new List<T>() { item };
}
There is no point to loop through the Employee List of one to add it to a TreeNode.
Related
I am successfully binding to a list of objects and setting this as the DataGridViews datasource. Definiting columns, at run time, which include the appropriate DataPropertyNames.
However I now need to add a list to my object class. The size of this list is dynamic, but always the same size for all instances of my object.
So my question is how can I create my DataGridViewTextBoxColumn to create a column for each items within this list?
Below is my object code, which has been simplified for this question. Within the languages Dictionary will be something like:
"English", "Hello"
"German", "Hallo"
"Spanish", "Hola"
Ideally the Key would appear as the column name.
Looking like this (each row is a StringItem):
public class StringItem
{
#region Attributes ########################################################################
string name; // String name, used to generate the enum for referencing
string comment; // Helpful description of the string item.
Dictionary<string, string> languages = new Dictionary<string, string>(); // For language strings.
#endregion
#region Public Functions ##################################################################
public string Name
{
get { return name; }
}
public string Comment
{
get { return comment; }
set { comment = value; }
}
public Dictionary<string, string> Languages
{
get { return languages; }
set { languages = value; }
}
#endregion
}
Update:
I believe the suggested link in the comments isn't trying to achieve quite the same thing, however it is useful.
I can see that by adding the follow code to my StringItem I can directly access the language dictionary doing myObj["English"]
public string this[string key]
{
get
{
return languages[key];
}
set
{
languages[key] = value;
}
}
However the DataPropertyName, for each column, doesn't quite work liek this. I assume it uses reflections? Can anyone confirm this and tell me if I can implement my own reflection, or whatever DataPropertyName is using, to get my dictionary item.
This is how I set up the columns:
DataGridViewColumn column = new DataGridViewTextBoxColumn();
column.DataPropertyName = "Name";
column.Name = "Name";
dgvStrings.Columns.Add(column);
foreach (string lang in ProjectSettings.Languages)
{
column = new DataGridViewTextBoxColumn();
column.DataPropertyName = lang; // <<<< THIS ISN'T WORKING.
column.Name = lang;
dgvStrings.Columns.Add(column);
}
You could iterate through your list and add a ColumnHeader for each item.
Example:
foreach ( var item in list )
{
someDataGridView.Columns.Add(item + "ColumnHeader", item);
}
Explanation:
You can add columns programmatically. The first argument being the Column name, item + "ColumnHeader" in this case and the 2nd is Column text eg. displayed text, in this case item so if it was German that would be the header.
You can create a DataTable containing columns for your class properties and languages, then after editing the data table in DataGridView, revert it back to List<StringItem>.
To do so, I suppose you have StringItem. I just refactored your code to make it more clean:
public class StringItem
{
public static string[] LanguageNames
{
get { return new[] { "English", "German", "Spanish" }; }
}
public StringItem(string name)
{
Name = name;
Languages = new Dictionary<string, string>();
LanguageNames.ToList().ForEach(x => Languages.Add(x, null));
}
public string Name { get; private set; }
public string Comment { get; set; }
public Dictionary<string, string> Languages { get; set; }
}
Then create methods to convert List<StringItem> to a DataTable and vice versa:
public static class StringItemExtensions
{
public static DataTable ToDataTable(this List<StringItem> list)
{
var dt = new DataTable();
dt.Columns.Add("Name").ReadOnly = true;
dt.PrimaryKey = new DataColumn[] { dt.Columns["Name"] };
dt.Columns.Add("Comment");
StringItem.LanguageNames.ToList().ForEach(x => dt.Columns.Add(x));
list.ForEach(item =>
{
var values = new List<object>();
values.Add(item.Name);
values.Add(item.Comment);
values.AddRange(item.Languages.Values.Cast<string>());
dt.Rows.Add(values.ToArray());
});
return dt;
}
public static List<StringItem> ToStringItemList(this DataTable table)
{
return table.AsEnumerable().Select(row =>
{
var item = new StringItem(row.Field<string>("Name"));
foreach (var lang in StringItem.LanguageNames)
item.Languages[lang] = row.Field<string>(lang);
return item;
}).ToList();
}
}
Now you can edit a List<StringItem> in DataGridView:
var list = new List<StringItem>();
list.Add(new StringItem("Key1"));
list.Add(new StringItem("Key2"));
list.Add(new StringItem("Key3"));
this.dataGridView1.DataSource = list.ToDataTable();
After finish editing, it's enough to export the data table to a List<StringItem>:
var list = ((DataTable)this.dataGridView1.DataSource).ToStringItemList();
I'm having some trouble getting an indexer to work with a List of objects. I'd like to be able to access each object in the list by it's name (a string) rather than it's index. So, I'd like to overload the [] operator to achieve this. So far I can't get the overload to show up in intellisense and it does not work at the moment. What I have so far is:
A singleton object of a class called MapInfo:
MapInfo mi = MapInfo.Instance;
In the MapInfo class I have a list of Table objects:
List<Table> tables;
public List<Table> Tables
{
get { return tables; }
}
And finally in the Tables class I have tried this:
class Table : IEnumerable<Table>
{
public Table this[string tableName]
{
get
{
foreach (Table table in this)
{
if (table.name == tableName)
return table;
}
return null;
}
}
I'd like to be able to access my Table objects using:
mi.Tables["SomeTableName"]
This is the first time I've tried this so I'm not quite sure where I'm going wrong.
you can use approach like this
public class MapInfo {
private readonly TableCollection _tables = new TableCollection();
public TableCollection Tables {
get { return _tables; }
}
}
public class TableCollection : List<Table> {
public Table this[string name] {
get { return this.FirstOrDefault(t => t.Name == name); /*using System.Linq;*/ }
}
}
public class Table {
public string Name { get; set; }
}
or simply use a dictionary (Dictionary<string, Table>) as Danaldo suggested. but not BOTH of them as he'd coded =))
IMO, the right way is not to use indexers like these, cause as I see there can be more than one table with 'unique' name in your collections. I'd recommend using a simple List of Tables and a method like GetTableWithName to make things clearer because indexers usually give a (false) hope that that your data is unique
OR you can replace a call to FirstOrDefault with SingleOrDefault which will internally ensure that if there is an element with the 'name' no other element has the same 'name'
You are overloading the indexer on the Table class. You got something wrong in your code structure. The Table class is an IEnumerable<Table>, so a Table contains other Tables.
So, a List<Table> will contain Table instances which in turn also contain Table instances.
With mi.Tables["SomeTableName"] you are trying to access the indexer of List, not the indexer of Table.
Why not define an indexer in MapInfo?
Use a dictionary
Private Dictionary<String, Table> tables;
public Dictionary<String, Table> Tables
{
get { return tables; }
}
Then:
class Table : IEnumerable<Table>
{
public Table this[string tableName]
{
get
{
Table table;
if(mi.tables.TryGetValue(tableName, out table))
{
return table;
}
else
{
return null;
}
}
}
}
Your implementation of MapInfo/Table isn't quite right to allow you to do what you want.
Your property of Tables in MapInfo can't be a list of tables, it has to be of type Table. Then your Table class should fully implement the IEnumerable interface.
Quick example written in LinqPad:
class Table : IEnumerable<Table>
{
public Table()
{
_tables = new Dictionary<string, Table>();
}
public string name { get; set; }
Dictionary<string, Table> _tables;
public void Add(string tname)
{
_tables.Add(tname, new Table { name = tname });
}
public Table this[string tableName]
{
get
{
Table table;
if (_tables.TryGetValue(tableName, out table))
return table;
return null;
}
}
public IEnumerator<Table> GetEnumerator()
{
return _tables.Values.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
void Main()
{
MapInfo mi = new MapInfo();
mi.Table["foo"].Dump();
}
class MapInfo
{
public MapInfo()
{
Tables = new Table();
test();
}
private void test()
{
Tables.Add("foo");
Tables.Add("bar");
Tables.Add("soom");
}
public Table Tables { get; set; }
}
I have a class called Employee. From my controller i am passing IEnumerable<Employee> to view. How to convert MongoDB.Driver.MongoCollection<Employee> to IEnumerable<Employee>?
public ActionResult Index()
{
var server = MongoServer.Create("mongodb://127.0.0.1");
var db = server.GetDatabase("employee");
var employeeCollection = new Collection<Employee>
{
new Employee
{
EmployeeId = new ObjectId(),
EmployeeName = "A"
},
new Employee
{
EmployeeId = new ObjectId(),
EmployeeName = "B"
}
};
var collection = db.GetCollection<Employee>("employee");
collection.InsertBatch(employeeCollection);
return View(collection);
}
You can just use FindAll and that will allow you to enumerate over the entire collection:
return View(collection.FindAll());
You could also use AsQueryable to achieve the same result:
return View(collection.AsQueryable());
But you should be careful about doing that. The collection could possibly contain millions of documents.
Currently, I am using something like this:
try
{
dr = SQL.Execute(sql);
if(dr != null) {
while(dr.Read()) {
CustomObject c = new CustomObject();
c.Key = dr[0].ToString();
c.Value = dr[1].ToString();
c.Meta = dr[2].ToString();
customerInfo.CustomerList.Add(c);
}
}
else
{
customerInfo.ErrorDetails="No records found";
}
Instead of me doing the assigments manually, is there a way to do this mapping directly (assume that the column names match with the field names).
One requirement, however is that I want to do this by my current approach of using sql queries and not by using pure LINQ based approaches. For one, the SQL queries are big enough, involve complex JOINs and have been tested thoroughly so I don't want to introduce more bugs at the moment. Any suggestions?
One simple solution would be to make a constructor for your CustomObject that takes a DataRow (from the example, so if it's another class, please correct me).
And in your new constructor, do as you do in your own example.
public CustomObject(DataRow row)
{
Key = row[0].ToString();
// And so on...
}
One other way would be to introduce generics, and make a new function in your SQL-class
Example (Took code from Passing arguments to C# generic new() of templated type):
// This function should reside in your SQL-class.
public IEnumerable<T> ExecuteObject<T>(string sql)
{
List<T> items = new List<T>();
var data = ExecuteDataTable(sql); // You probably need to build a ExecuteDataTable for your SQL-class.
foreach(var row in data.Rows)
{
T item = (T)Activator.CreateInstance(typeof(T), row);
items.Add(item);
}
return items;
}
Example usage:
public IEnumerable<CustomObject> GetCustomObjects()
{
return SQL.ExecuteObject<CustomObject>("SELECT * FROM CustomObject");
}
I have tested this code in LinqPad, it should work.
You can achieve by creating a generic method for your requirement. Also you can make your new method as the extension for the data table.
public static List<T> ToList<T>(this DataTable table) where T : class, new()
{
try
{
List<T> list = new List<T>();
foreach (var row in table.AsEnumerable())
{
T obj = new T();
foreach (var prop in obj.GetType().GetProperties())
{
try
{
PropertyInfo propertyInfo = obj.GetType().GetProperty(prop.Name);
propertyInfo.SetValue(obj, Convert.ChangeType(row[prop.Name], propertyInfo.PropertyType), null);
}
catch
{
continue;
}
}
list.Add(obj);
}
return list;
}
catch
{
return null;
}
}
}
Usage:
DataTable dtCustomer = GetCustomers();
List<CustomObject> CustomObjectList = dtCustomer.ToList<CustomObject>();
You should look into MicroORMs. Unlike regular ORMs, that provide an SDL you must use, MicroORMs allow you to use your own SQL queries and only provide the mapping from SQL result sets to C# objects and from C# objects to SQL parameters.
My favorite is PetaPoco, which also provides a query builder that uses your own SQL but does some neat manipulation of parameter numbers.
#user1553525's answer is great, however, if your column names do not match up exactly with your property names it does not work.
So first you would want to create a custom attribute. Then use the attribute in your class that you are trying to deserialize, finally, you want to deserialize the DataTable.
Custom Attribute
We create a custom attribute that will be applied to the properties inside of our class. We create the class to have the property Name that we will use later to get the correct column from our DataTable.
[AttributeUsage(AttributeTargets.Property, Inherited = false)]
public class MySqlColName : Attribute
{
private string _name = "";
public string Name { get => _name; set => _name = value; }
public MySqlColName(string name)
{
_name = name;
}
}
Class to deserialize
Next, in the class that we are going to populate, we are going to declare the column names that will link to the properties in the class using the attribute [MySqlColName] that we just created.
However, if the property name is the same as the database column we do not need to specify the column name in an attribute because the .ToList<>() function will assume the name of the column from the properties name.
public class EventInfo
{
[MySqlColName("ID")]
public int EventID { get; set; }
//Notice there is no attribute on this property?
public string Name { get; set; }
[MySqlColName("State")]
public string State { get; set; }
[MySqlColName("Start_Date")]
public DateTime StartDate { get; set; }
[MySqlColName("End_Date")]
public DateTime EndDate { get; set; }
}
DataTable ToList Extension Method
Finally, we modify #user1553525's answer by adding in a check to see if our custom attribute has been provided. If it is then we set the name of the column to the name provided, otherwise, we use the property name (see code inside of the try block).
public static List<T> ToList<T>(this DataTable table) where T : class, new()
{
try
{
List<T> list = new List<T>();
foreach (var row in table.AsEnumerable())
{
T obj = new T();
foreach (var prop in obj.GetType().GetProperties())
{
try
{
//Set the column name to be the name of the property
string ColumnName = prop.Name;
//Get a list of all of the attributes on the property
object[] attrs = prop.GetCustomAttributes(true);
foreach (object attr in attrs)
{
//Check if there is a custom property name
if (attr is MySqlColName colName)
{
//If the custom column name is specified overwrite property name
if (!colName.Name.IsNullOrWhiteSpace())
ColumnName = colName.Name;
}
}
PropertyInfo propertyInfo = obj.GetType().GetProperty(prop.Name);
//GET THE COLUMN NAME OFF THE ATTRIBUTE OR THE NAME OF THE PROPERTY
propertyInfo.SetValue(obj, Convert.ChangeType(row[ColumnName], propertyInfo.PropertyType), null);
}
catch
{
continue;
}
}
list.Add(obj);
}
return list;
}
catch
{
return null;
}
}//END METHOD
Usage
Finally, we can call the .ToList<>() method and get a list of serialized objects
List<EventInfo> CustomObjectList;
using (DataTable dtCustomer = GetDataTable("SELECT * FROM EventIndex"))
{
CustomObjectList = dtCustomer.ToList<EventInfo>();
}
Side Note: I have a few custom methods that I used
public static bool IsNullOrWhiteSpace(this string x)
{
return string.IsNullOrWhiteSpace(x);
}
public static DataTable GetDataTable(string Query)
{
MySqlConnection connection = new MySqlConnection("<Connection_String>");
try
{
DataTable data = new DataTable();
connection.Open();
using (MySqlCommand command = new MySqlCommand(Query, connection))
{
data.Load(command.ExecuteReader());
}
return data;
}
catch (Exception ex)
{
// handle exception here
Console.WriteLine(ex);
throw ex;
}
finally
{
connection.Close();
}
}
Assumption: if you need objects only for serialization or simple ad-hoc output.
You can use ExpandoObject and SqlDataReader.GetSchemaTable() like this:
private IEnumerable<dynamic> ReaderToAnonymmous(SqlCommand comm) {
using (var reader = comm.ExecuteReader()) {
var schemaTable = reader.GetSchemaTable();
List<string> colnames = new List<string>();
foreach (DataRow row in schemaTable.Rows) {
colnames.Add(row["ColumnName"].ToString());
}
while (reader.Read()) {
var data = new ExpandoObject() as IDictionary<string, Object>;
foreach (string colname in colnames) {
var val = reader[colname];
data.Add(colname, Convert.IsDBNull(val) ? null : val);
}
yield return (ExpandoObject)data;
}
}
}
Although there are posted faster solutions (i posted this as alternative lazy approach for ad-hoc SQL/Reader results/outputs).
The following function accepts a SQL string and an object, it requires the object to have a property for each column in the select statement. The object must be instantiated.
public object SqlToSingleObject(string sSql, object o)
{
MySql.Data.MySqlClient.MySqlDataReader oRead;
using (ConnectionHelper oDb = new ConnectionHelper())
{
oRead = oDb.Execute(sSql);
if (oRead.Read())
{
for (int i = 0; i < oRead.FieldCount; i++)
{
System.Reflection.PropertyInfo propertyInfo = o.GetType().GetProperty(oRead.GetName(i));
propertyInfo.SetValue(o, Convert.ChangeType(oRead[i], propertyInfo.PropertyType), null);
}
return o;
}
else
{
return null;
}
}
}
When searching for this answer I found that you can use Dapper library: https://dapper-tutorial.net/knowledge-base/44980945/querying-into-a-complex-object-with-dapper
You can use something like this:
using (var connection = new SqlConnection(ConnectionString))
{
connection.Open();
IList<CustomObject> result = connection.Query<CustomObject>(sql, commandType: CommandType.Text).ToList();
}
Although this question has been around I could not find a clean solution to this. For my purpose I came up with the following which works quite well in my case.
using System.Dynamic;
private IEnumerable<ExpandoObject> GetQueryToList()
{
try
{
using (var conn = new SqlConnection(ConnectionString))
using (var cmd = new SqlCommand(MyQuery, conn))
{
var list = new List<ExpandoObject>();
conn.Open();
var reader = cmd.ExecuteReader();
while (reader.Read())
{
var expandoObject = new ExpandoObject();
for (var i = 0; i < reader.FieldCount; i++)
{
((IDictionary<string, object>) expandoObject).Add(
reader.GetName(i), reader[i]);
}
list.Add(expandoObject);
}
reader.Close();
return list;
}
}
catch (Exception ex)
{
var m = MethodBase.GetCurrentMethod();
Console.WriteLine(ex.Message + " " + m.Name);
}
return null;
}
If i have a list of objects and i want to move to the next node with each function call (ie create a "GetNextNode" how would i go about doing this? Right now i have one method which will get the first node of my List and set the currentObj to it and return it (leaving previous node still at null) a flag indicates that we're not dealing with the first node in the list anymore. then i move forward and i want to iterate through the list (using foreach i guess?) to one node past my currentObj. Here is my code:
List<Employee> ListOfEmployees = new List<Employee>();
Employee currEmployeeObj = null;
Employee prevEmployeeObj = null;
foreach (Employee employee in ListOfEmployees)
{
//how do i keep track of the previous and current employee in here?
}
return (currEmployeeObj);
}
I hate to sound like a dinosaur, but since you're implementing with a List anyway, why not iterate over it with for instead of foreach? Integers are really useful for comparisons like i == j + 1
Looks like you really are re-inventing an enumerator:
public IEnumerator<Employee> GetEmployees()
{
foreach (Employee employee in ListOfEmployees)
{
//custom processing here
yield return employee;
}
}
Usage:
var myEnumerator = foo.GetEmployees();
while(myEnumerator.MoveNext())
{
var someEmployee = myEnumerator.Current;
//do something
}
Just as an update here is the full class implementation so you can verify it compiles and works..
public class Foo
{
List<Employee> ListOfEmployees = new List<Employee>();
public Foo()
{
ListOfEmployees.Add(new Employee());
}
public IEnumerator<Employee> GetEmployees()
{
foreach (Employee employee in ListOfEmployees)
yield return employee;
}
}
(As an academic exercise, the other answers are probably more appropriate here: )
You could create an extension method like so:
public static IEnumerable<Tuple<T, T>> ToPairs<T>(this IEnumerable<T> enumerable)
{
using (var enumerator = enumerable.GetEnumerator())
{
if (enumerator.MoveNext())
{
var previous = enumerator.Current;
while (enumerator.MoveNext())
{
var current = enumerator.Current;
yield return new Tuple<T, T>(previous, current);
previous = current;
}
}
}
}
To return you a tuple containing pairs of elements.
Which would be used like:
foreach (var pair in ListOfEmployees.ToPairs())
{
Employee prevEmployee = pair.Item1;
Employee currEmployeeObj = pair.Item2;
}
http://msdn.microsoft.com/en-us/library/system.collections.ienumerator.aspx
The link above this line of text has what will work to solve my issue.
Thanks all for the responses and help! Upvoted those who tried to help and had something to offer