List<DataRow> to custom List<Model> - c#

I have used the following code:
ManageUser userObj = new ManageUser();
List<StoreUserList> storeList = new List<StoreUserList>();
StoreUserList sl = new StoreUserList();
foreach (var items in userObj.GetStoreUserList(Username, Password))
{
sl.UserName = items[0].ToString();
sl.EmailId = items[1].ToString();
sl.FirstName = items[2].ToString();
sl.LastName = items[3].ToString();
sl.BadLoginCount = Convert.ToInt32(items[4]);
sl.ManagerName = items[5].ToString();
storeList.Add(sl);
}
StoreUserList is my Model in which i have defined all the properties.
ManagerUser is the class which will return the data in the form of List<DataRow>.
So, i order to populate the a generic list of my model type i have used the above code, to do it.
But, as you can see i have hard-coded all the values to bind the model and then added that model to the list. I just wanted to know that is there some other way to do it or some magic way to do it in easy?

If you can modify ManageUser.GetStoreUserList a little to return DataTable instead of List<DataRow>, then you can use Automapper, which can map IDataReader implementations to a collection of objects.
Suppose you have this entity type:
class User
{
public int Id { get; set; }
public string Name { get; set; }
}
Then you can convert DataTable to the List<User> this way:
// sample data table
var dataTable = new DataTable();
dataTable.Columns.Add("Id", typeof(int));
dataTable.Columns.Add("Name", typeof(string));
dataTable.Rows.Add(1, "John");
dataTable.Rows.Add(2, "Mary");
dataTable.Rows.Add(3, "Peter");
dataTable.Rows.Add(4, "Sarah");
// fill users list
using (var reader = dataTable.CreateDataReader())
{
var users = Mapper.Map<IDataReader, List<User>>(reader);
}

You can use automapper project for this. See more details here:
How to use AutoMapper to map a DataRow to an object in a WCF service?
And here: Map RowDataCollection to DTO using AutoMapper

I think your code will not add multiple StoreUserList object in the list but will add only 1 object with the value as last datarow. What you need to do is
ManageUser userObj = new ManageUser();
List<StoreUserList> storeList = new List<StoreUserList>();
StoreUserList sl = null;
foreach (var items in userObj.GetStoreUserList(Username, Password))
{
sl = new StoreUserList();
sl.UserName = items[0].ToString();
sl.EmailId = items[1].ToString();
sl.FirstName = items[2].ToString();
sl.LastName = items[3].ToString();
sl.BadLoginCount = Convert.ToInt32(items[4]);
sl.ManagerName = items[5].ToString();
storeList.Add(sl);
}
Otherwise i dont see any problem in your code.

Related

How does DataGridView get the list of properties in a class?

When you set a List or, even better, a BindingList as the source of a DataGridView, it gets the list of properties and uses them to generate columns.
If I do the following:
StatsList = new BindingList<ExpandoObject>();
// Add seed record to generate columns in DataGridView
dynamic ds = new ExpandoObject();
ds.key = "0000";
ds.Name = "Bob";
ds.Number = "2255442";
ds.key = "0001";
ds.Name = "Nathan";
ds.Number = "1217479";
StatsList.Add(ds);
dgvListView.DataSource = StatsList;
Nothing happens. No columns or rows are ever added to the DataGridView. I'm guessing that's because there is a method missing that allows the DataGridView to get the collection of properties.
If I run the same code but replace ExpandoObject with MyCustomClass in the code sample above, as defined below, the DataGridView would populate just fine.
public class MyCustomClass
{
public string key { get; set; }
public string name { get; set; }
public string number { get; set; }
}
However, since I'm reading my data from a source that may change, I need to use a dynamic class. I've tried a number of methods including a BindingList BindigList> none of which will bind the dynamic members to column. I've even tried creating a class that inherits DynamicObject with overrides for TryGetMember and TrySetMember.
I feel like I'm spinning my wheels and overlooking a simple solution. Maybe there's some trick using a Dictionary and Linq that I'm missing here.
Caveats: I'm collecting data, summarizing it and displaying it. I don't need to add or remove data once it's been displayed. I will be trying to filter and sort it though. I'll cross that bridge, though, once I figure out how to show it.
My example shows simple known values and types, but my actual source data will be less predictable and it is parsed from JSON using Newtonsoft JSON with a structure as follows:
[
{
"match_number": 1,
"set_number": 1,
"key": "2017onbar_f1m1",
"score_breakdown": {
"blue": {
"totalPoints": 236,
"foulCount": 1,
"adjustPoints": 0,
"overtime": true
},
"red": {
"totalPoints": 236,
"foulCount": 1,
"adjustPoints": 0,
"overtime": true
}
},
"teammembers": {
"blue": {
"teams": [
"member1",
"member2",
"member3"
]
},
"red": {
"teams": [
"member4",
"member5",
"member6"
]
}
}
}]
However, the score_breakdown fields will vary.
Try converting your BindingList into DataTable
protected void Page_Load(object sender, EventArgs e)
{
var StatsList = new BindingList<ExpandoObject>();
// Add seed record to generate columns in DataGridView
dynamic ds = new ExpandoObject();
ds.key = "0000";
ds.Name = "Bob";
ds.Number = "2255442";
dynamic ds2 = new ExpandoObject();
ds2.key = "0001";
ds2.Name = "Nathan";
ds2.Number = "1217479";
StatsList.Add(ds);
StatsList.Add(ds2);
GridView1.DataSource = ExpandoListToDataTable(StatsList);
GridView1.DataBind();
}
protected DataTable ExpandoListToDataTable(BindingList<ExpandoObject> d)
{
var dt = new DataTable();
foreach (var a in d)
{
foreach (var key in ((IDictionary<string, object>)a).Keys)
{
if (!dt.Columns.Contains(key))
{
dt.Columns.Add(key);
}
}
dt.Rows.Add(((IDictionary<string, object>)a).Values.ToArray());
}
return dt;
}
In case when given datasource is instance of some type (not instance of Type) DatagridView.DataSource setter will use source.GetType().GetProperties() for retrieving properties, which will be used for generating columns.
Problem is that in your case type ExpandoObject will return empty collection
dynamic ds = new ExpandoObject();
ds.key = "0000";
ds.Name = "Bob";
ds.Number = "2255442";
ds.GetType().GetProperties() // return empty collection
Because you know name of properties in compile time you can use new features of C# and create tuple
var ds = ( key: "0000", Name: "Bob", Number: "2255442" );
But again - if you know properties names at compile time why not create a class for it?

How to save query into dictionary in c#?

I am using Visual Studio 2012 and C#. I have a problem: I want to collect all results of a SQL query into dictionary.
This is my code:
Dictionary<int,List<string>> dic = new Dictionary<int, List<string>>();
string query = "select request_number, service_category ,service_type from enugro.enugro_service_requests_info;";
MySqlConnection connec = new MySqlConnection(strcon);
connec.Open();
MySqlCommand cmd = new MySqlCommand(query,connec);
MySqlDataReader reader = cmd.ExecuteReader();
while(reader.Read())
{
dic.Add((int)reader["request_number"], { reader["service_category"], reader["service_type"] });
}
// display result
foreach(var disp in dic)
{
MessageBox.Show(disp.Key.ToString()+"= "+disp.Value.ToString());
}
As you can see my SQL query returns 3 columns to be retrieved and to store into dictionary. Could you help me?
Since the type of the dictionary values is List<string>, then you need to create a new list for each row and add it to the dictionary like this:
dic
.Add(
(int)reader["request_number"],
new List<string>
{
reader["service_category"].ToString(),
reader["service_type"].ToString()
});
Consider creating a class to hold the information that you want from each row and use it as the type for the dictionary values. Such class would look something like this:
public class MyValue
{
public string ServiceCategory {get;set;}
public string ServiceType {get;set;}
}
Then you can have your dictionary be of type Dictionary<int, MyValue> which will allow you to do this:
dic
.Add(
(int)reader["request_number"],
new MyValue
{
ServiceCategory = reader["service_category"].ToString(),
ServiceType = reader["service_type"].ToString()
});
Why would you do that? You can create DTO (data transfer object) with properties according to column names in database, and bind query result to it. Then you can return list of DTO objects. It would be better solution, then complicating with dictionary.

How to add multiple data types to list

I have my list as below,
var serie_line = new { name = series_name , data =new List<float?>() };
In the above code data in another list which contains float value, I want data to contains two different datatype value that is string and float value, when I am trying to add two different datatype values as follow,
var serie_line = new { name = series_name , data =new List<string, float?>() };
It gives me an error as
Using the generic type'System.Collections.Generic.List<T>' requires 1 argument.
I cannot try for data=new List<Tupple<string,float>>();..since I am using .NET 3.5...any idea..hw cn I deal with this problem..thank you,
----------Updated question---------
Output that I requires is as follows,
{
"legend":{"enabled":"true"},
"title":{"text":"Financial"},
"chart":{"type":"pie"},
"series":
[
{"name":"Actual-","data":[["Market Share",20.00],["Sales Growth",30.00],["Operating Profit",40.00],["Actual-Gross Margin %",10.00]]}
]
},
this data list should contains one string value and one float value...I want to draw pie chart in highcharts but output I am getting is as follows,
{
"legend":{"enabled":"true"},
"title":{"text":"Financial"},
"chart":{"type":"column"},
"series":[{"name":"Actual","data":[{"Str":"Market Share","Flo":20.00}]},
{"name":"Actual","data":[{"Str":"Sales Growth","Flo":30.00}]},
{"name":"Actual","data":[{"Str":"Operating Profit","Flo":40.00}]},
{"name":"Actual","data":[{"Str":"Gross Margin %","Flo":10.00}]}
]
}
Any Idea...???
----------Use of Dictionary----------
var data = new Dictionary<string, float?>();
var serie_line = new { name = series_name, data };
serie_line.data.Add(child_object_name, period_final_value);
but this doesnot give required output...
it only gives values inside data as for eg,
"data":["market share":20.00].. since I am serializing serie_line into JSON...but I don't want this way..what I want is "data":["market share",20.00]
I hope u get this...
just use
new Dictionary<string, float?>() //if your string value cannot be duplicated
//or
new List<KeyValuePair<string,float?> >
create a type to be use with your list:
public class MyDataType
{
public string Str {get; set;}
public float? Flo {get;set;}
}
you use it like this:
var serie_line = new { name = series_name , data =new List<MyDataType>() };
serie_line.data.Add(new MyDataType{Flo = 45.4});
or like:
var serie_line = new { name = series_name , data =new List<MyDataType>() };
serie_line.data.Add(new MyDataType{Flo = 45.4, Str = "my string"});
If you are trying to add items to a list so that both are available you need to use List<object>(), as its the only shared type between both. That or use ArrayList.
As you pull the objects out you will need to test if they are objects of type string or float? in order to cast them back. You may be able to wrap them.
Use ArrayList, the non-generic version of List.
How about something more structured:
public struct MyData
{
public float? FloatData;
public string StringData;
}
var serie_line = new
{
name = series_name,
data = new MyData()
{
FloatData = theFloatData,
StringData = theStringData,
}
};

LINQ and creating NON anonymous return values

I think I understand returning records of an anonymous type from But in this I want to create NEW CatalogEntries, and set them from the values selected. (context is a Devart LinqConnect database context, which lets me grab a view).
My solution works, but it seems clumsy. I want to do this in one from statement.
var query = from it in context.Viewbostons
select it;
foreach (GPLContext.Viewboston item in query)
{
CatalogEntry card = new CatalogEntry();
card.idx = item.Idx;
card.product = item.Product;
card.size = (long)item.SizeBytes;
card.date = item.Date.ToString();
card.type = item.Type;
card.classification = item.Classification;
card.distributor = item.Distributor;
card.egplDate = item.EgplDate.ToString();
card.classificationVal = (int)item.ClassificationInt;
card.handling = item.Handling;
card.creator = item.Creator;
card.datum = item.Datum;
card.elevation = (int)item.ElevationFt;
card.description = item.Description;
card.dirLocation = item.DoLocation;
card.bbox = item.Bbox;
card.uniqID = item.UniqId;
values.Add(card);
}
CatalogResults response = new CatalogResults();
I just tried this:
var query2 = from item in context.Viewbostons
select new CatalogResults
{ item.Idx,
item.Product,
(long)item.SizeBytes,
item.Date.ToString(),
item.Type,
item.Classification,
item.Distributor,
item.EgplDate.ToString(),
(int)item.ClassificationInt,
item.Handling,
item.Creator,
item.Datum,
(int)item.ElevationFt,
item.Description,
item.DoLocation,
item.Bbox,
item.UniqId
};
But I get the following error:
Error 79 Cannot initialize type 'CatalogService.CatalogResults' with a
collection initializer because it does not implement
'System.Collections.IEnumerable' C:\Users\ysg4206\Documents\Visual
Studio
2010\Projects\CatalogService\CatalogService\CatalogService.svc.cs 91 25 CatalogService
I should tell you what the definition of the CatalogResults is that I want to return:
[DataContract]
public class CatalogResults
{
CatalogEntry[] _results;
[DataMember]
public CatalogEntry[] results
{
get { return _results; }
set { _results = value; }
}
}
My mind is dull today, apologies to all. You are being helpful. The end result is going to be serialized by WCF to a JSON structure, I need the array wrapped in a object with some information about size, etc.
Since .NET 3.0 you can use object initializer like shown below:
var catalogResults = new CatalogResults
{
results = context.Viewbostons
.Select(it => new CatalogEntry
{
idx = it.Idx,
product = it.Product,
...
})
.ToArray()
};
So if this is only one place where you are using CatalogEntry property setters - make all properties read-only so CatalogEntry will be immutable.
MSDN, Object initializer:
Object initializers let you assign values to any accessible fields or properties of an
object at creation time without having to explicitly invoke a constructor.
The trick here is to create a IQueryable, and then take the FirstOrDefault() value as your response (if you want a single response) or ToArray() (if you want an array). The error you are getting (Error 79 Cannot initialize type 'CatalogService.CatalogResults' with a collection initializer because it does not implement 'System.Collections.IEnumerable') is because you're trying to create an IEnumerable within the CatalogEntry object (by referencing the item variable).
var response = (from item in context.Viewbostons
select new CatalogEntry()
{
idx = item.Idx,
product = item.Product,
size = (long)item.SizeBytes,
...
}).ToArray();
You don't have to create anonymous types in a Linq select. You can specify your real type.
var query = context.Viewbostons.Select( it =>
new CatalogEntry
{
idx = it.idx,
... etc
});
This should work:
var query = from it in context.Viewbostons
select new CatalogEntry()
{
// ...
};

LINQ Query to get Column Headers in Silverlight

I am working on a Silverlight application using a WCF service where I need to get all the Column Headers from a specific table. I have been trying to write a LINQ query to do this, but so far I have not been able to get it to work correctly. I have not found very much information pertaining to this. I have found the following information, but I have had difficulties connecting to my data.
http://www.c-sharpcorner.com/UploadFile/dhananjaycoder/4856/#ReadAndPostComment
So far I have tried the following...This will not compile due to DataContext needing a parameter and that is where I am stuck.
public List<string> GetColumnHeaders()
{
DataContext context = new DataContext();
List<string> columnList = new List<string>();
var dataModel = context.Mapping;
foreach (var r in dataModel.GetTables())
{
if (r.TableName.Equals("table1", StringComparison.InvariantCultureIgnoreCase))
{
foreach (var c in r.RowType.DataMembers)
{
columnList.Add(c.MappedName);
}
}
}
return columnList;
}
Instead of using DataContext context = new DataContext();
I tried the following, but I know the problem is the same.
var dataModel = new AttributeMappingSource()
.GetModel(
typeof(RepositoryBase<HBS_SondesEntities>
));
Here is my best attempt at a solution, its hard to really understand what you have tried/written.
public List<string> GetColumnHeaders(){
List<string> columnList = new List<string>();
using (SondesEntities context = new HBS_SondesEntities()){
foreach (var r in context.Mapping.GetTables()){
if (r.TableName
.Equals("table1", StringComparison.InvariantCultureIgnoreCase)) {
foreach (var c in r.RowType.DataMembers){
columnList.Add(c.MappedName);
}
}
}
}
return columnList;
}
Assuming I didn't fat finger something here is the same code using linq.
public List<string> GetColumnHeaders(){
List<string> columnList = new List<string>();
using (SondesEntities context = new HBS_SondesEntities()){
var query = (
context.Mapping.GetTables()
.Where(t=>t.TableName
.Equals(
"table1",
StringComparison.InvariantCultureIgnoreCase)
)
).SelectMany(x=>x.RowType.DataMembers);
columnList = query.Select(m=>m.MappedName).ToList()
}
return columnList;
}
This might help:
http://jesseliberty.com/2009/08/13/linq-for-silverlight-developers/
I'm not sure what you mean by table, but if its a datagrid, the link should help.

Categories