Filling class object from multiple dataset - c#

I want to fill the Organism class object with data from OrganismDT and AntibioticDT. below are the Datatable structure,Class structure and the filling technique i am trying. based on the OrganismAliasId i have to fill the AntibioticsDT[Antibiotic objects list] rows and assign to corresponding Organisms object. Could any one please help me to achieve this..
DataTable
DataTable OrganismDT = new DataTable("Organism");
OrganismDT.Columns.Add("OrganismAliasId");
OrganismDT.Columns.Add("Name");
DataTable AntibioticsDT = new DataTable("Antibiotics");
AntibioticsDT.Columns.Add("OrganismAliasId");
AntibioticsDT.Columns.Add("Name");
AntibioticsDT.Columns.Add("Susceptibility");
Class Structure:
public class Organism
{
public int AliasId { get; set; }
public string Name { get; set; }
public List<Antibiotic> Antibiotics;
}
public class Antibiotic
{
public string Name { get; set; }
public string Susceptibility { get; set; }
}
filling Datatable table to Class object
List<Organism> Organisms = Organism.AsEnumerable().Select(row =>
new Organism
{
AliasId = row.Field<int>("OrganismAliasId"),
Name = row.Field<string>("Name"),
Antibiotics = new List<Antibiotic>()
}).ToList();

This should work:-
List<Organism> Organisms = OrganismDT.AsEnumerable().Select(row =>
new Organism
{
AliasId = row.Field<int>("OrganismAliasId"),
Name = row.Field<string>("Name"),
Antibiotics = AntibioticsDT.AsEnumerable()
.Where(x => x.Field<int>("OrganismAliasId")
== row.Field<int>("OrganismAliasId"))
.Select(x => new Antibiotic
{
Name = x.Field<string>("Name"),
Susceptibility = x.Field<string>("Susceptibility")
}).ToList()
}).ToList();
Also, Please note you should create the columns by specifying their datatypes otherwise it may throw run-time exception:-
AntibioticsDT.Columns.Add("OrganismAliasId",typeof(int));

Related

C# group by a column and form hierarchical data with other columns

I am trying to group a column and form the the rest of the columns as child, hierarchical data:
I am trying to group by Code and form the parent and child relationship from a flat list, below is the hierarchical data I am trying to form:
source list:
public class ItemAssignmentFlatList
{
public int Code { get; set; }
public string Type { get; set; }
public string Description { get; set; }
public int ItemCode{ get; set; }
public DateTime EffectiveDate{ get; set; }
public string Area{ get; set; }
public string TaxCode{ get; set; }
public string LocationId { get; set; }
}
Need to convert above flat list into below List of hierarchical data:
public class ItemInfo
{
public int Code { get; set; }
public string Type { get; set; }
public string Description { get; set; }
public List<TaxInfo> TaxPlan { get; set; }
}
public class TaxPlan
{
public int ItemCode{ get; set; }
public DateTime EffectiveDate{ get; set; }
public string Area{ get; set; }
public string TaxCode{ get; set; }
public string LocationId { get; set; }
}
Need hierarchical list with above flat data list with C# extension methods.
I have below code, but looking for clean code to reduce number of lines:
var items= results.GroupBy(x => new { x.Code, x.Type });
List<ItemInfo> result = new List<ItemInfo>();
foreach (var group in items)
{
var taxPlans = group.
Select(y => new TaxPlan
{
TaxArea = y.TaxArea,
ItemCode = y.ItemCode
});
var itemInfo= new ItemInfo
{
Code = group.FirstOrDefault().Code,
Type = group.FirstOrDefault().Type,
Description = group.FirstOrDefault().Description,
TaxPlan = taxPlans.ToList()
};
result.Add(itemInfo);
}
Something like this?:
var input = new List<ItemAssignmentFlatList>(){
new ItemAssignmentFlatList{
Code = 1,
Area = "a"
},
new ItemAssignmentFlatList{
Code = 1,
Area = "b"
},
new ItemAssignmentFlatList{
Code = 2,
Area = "c"
}
};
input
.GroupBy(
x => x.Code,
(int code, IEnumerable<ItemAssignmentFlatList> items) =>
{
var first = items.FirstOrDefault();
var key = new ItemInfo
{
Code = first.Code
//, ...
};
var plan = items.
Select(y => new TaxPlan
{
Area = y.Area
//, ...
});
return new
{
key = key,
items = plan
};
}
).Dump();
Whenever you have a sequence of similar object, and you want to make "Items with their SubItems", based on common properties in your source sequence, consider to use one of the overloads of Enumerable.GroupBy
Because you don't just want "Groups of source items" but you want to specify your output, consider to use the overload that has a parameter resultSelector.
parameter keySelector: what should all elements in a group have in common
parameter resultSelector: use the common thing, and all elements that have this common thing to make one output element.
.
IEnumerable<ItemAssignmentFlatList> flatItemAssignments = ...
IEnumerable<ItemInfo> items = flatItemAssignments
// make groups with same {Code, Type, Description}
.GroupBy(flatItemAssignment => new {Code, Type, Description},
// parameter resultSelector: take the common CodeTypeDescription,
// and all flatItemAssignments that have this common value
// to make one new ItemInfo
(codeTypeDescription, flatItemAssignmentsWithThisCodeTypeDescription) => new ItemInfo
{
Code = codeTypeDescription.Code,
Type = codeTypeDescription.Type,
Description = codeTypeDescription.Description,
TaxPlans = flatItemAssignmentsWithThisCodeTypeDescription
.Select(flatItemAssignment => new TaxPlan
{
ItemCode = flatItemAssignment.ItemCode,
EffectiveDate = flatItemAssignment.EffectiveDate,
Area = flatItemAssignment.Area,
...
})
.ToList(),
});

Order dynamic list of object by common property with pagination

I have a several types of objects, which are I get from database, with different models that have common date property. I want to sort all this models but pull out data only in the end by pages.
Problem is when I add all IQueryable models in dynamic list they pull out all data from database and if there hundreds of records it will take a long time.
What I have already done:
Created base interface and in models inherited from him.
Added all models in dynamic list.
Sort this list by common property.
Added pages with X.PagedList
Example code:
Models:
private interface BaseInfo
{
public DateTime Date { get; set; }
}
public class CompanyEvent : BaseInfo
{
public string Description { get; set; }
public DateTime Date { get; set; }
}
public class SummerEvent : BaseInfo
{
public bool IsActive { get; set; }
public DateTime Date { get; set; }
public string MainPartner { get; set; }
}
public class Birthday : BaseInfo
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public DateTime Date { get; set; }
}
Code
public IPagedList<dynamic> GetDynamicInfo(int pageNumber)
{
int pageSize = 10;
List<dynamic> dynamicList = new List<dynamic>();
IQueryable<CompanyEvent> companyEvents = Employees.Select(a => new CompanyEvent()
{
Date = a.EventDate,
Description = a.Description
});
IQueryable<SummerEvent> summerEvents = Employees.Select(a => new SummerEvent()
{
Date = a.StartEventDate,
IsActive = a.IsActive,
MainPartner = a.Partner.Name
});
IQueryable<Birthday> birthdays = Employees.Select(a => new Birthday()
{
Date = a.DateBorn,
FirstName = a.FirstName,
a.LastName = a.LastName
});
dynamicList.AddRange(companyEvents);
dynamicList.AddRange(summerEvents);
dynamicList.AddRange(birthdays);
var sortedDynamicList = dynamicList.OrderBy(a => ((BaseInfo)a).Date).ToPagedList(pageNumber, pageSize);
return sortedDynamicList;
}
As you are using ToPageList that is bringing all the data in memory. You can use Skip and Take which will get translated to respective sql and do pagination on DB side like:
var sortedDynamicList = dynamicList
.OrderBy(a => ((BaseInfo)a).Date)
.Skip(pageNumber)
.Take(pageSize)
.ToPagedList(pageNumber, pageSize);
Secondly instead of using IPagedList<dynamic> you can use IPagedList<BaseInfo> that will also work same way.
UPDATE:
As in this scenario data is getting loaded from different tables and even different columns in them, one possible way is to load them separately each page of them and then do sort and paging on those like:
IQueryable<CompanyEvent> companyEvents = Employees.Skip(pageNumber).Take(pageSize)
.Select(a => new CompanyEvent()
{
Date = a.EventDate,
Description = a.Description
});
IQueryable<SummerEvent> summerEvents = Employees.Skip(pageNumber).Take(pageSize)
.Select(a => new SummerEvent()
{
Date = a.StartEventDate,
IsActive = a.IsActive,
MainPartner = a.Partner.Name
});
IQueryable<Birthday> birthdays = Employees.Skip(pageNumber).Take(pageSize)
.Select(a => new Birthday()
{
Date = a.DateBorn,
FirstName = a.FirstName,
a.LastName = a.LastName
});
and then do the following:
dynamicList.AddRange(companyEvents);
dynamicList.AddRange(summerEvents);
dynamicList.AddRange(birthdays);
var sortedDynamicList = dynamicList.OrderBy(a => ((BaseInfo)a).Date)
.ToPagedList(pageNumber, pageSize);

Populate multiple objects from JSON

I have this code:
public class Customer
{
Int32 id { get; set; } = 0;
User user1 { get; set; } = null;
User user2 { get; set; } = null;
}
/* ... */
using (MySqlConnection conn = new MySqlConnection(Costanti.connessione))
{
conn.Open();
MySqlCommand m = new MySqlCommand("
SELECT c1.*, u1.*, u2.*
FROM customers as c1
inner join utenti u1 on u1.customer_id = c1.id
inner join utenti u2 on u2.customer_id = c1.id
", conn);
MySqlDataReader x = m.ExecuteReader();
DataTable dataTable = new DataTable();
dataTable.Load(x);
String json_string = Newtonsoft.Json.JsonConvert.SerializeObject(dataTable);
List<Customer> lista = new List<Customer>();
Newtonsoft.Json.JsonConvert.PopulateObject(json_string, lista);
conn.Close();
}
How could I map c1.* fields of select to a generic Customer customer_1, and then u1.* and u2.* into customer_1's properties? Newtonsoft.Json.JsonConvert.PopulateObject doesn't let me do it.
The intermediate json_string looks like:
[
{
"id":1,
"id_user":8,
"name":"manuel",
"id_user1":2,
"name1":"michael"
},
{
"id":2,
"id_user":3,
"name":"friedrich",
"id_user1":6,
"name1":"antony"
}
]
And the result has to be a list composed by:
Customer(with id=1), with User1(8,"manuel") and User2(2,"michael");
Customer(with id=2), with User1(3,"friedrich") and User2(6,"antony").
The main reason your call to PopulateObject() is not working is that your c# data model does not match the schema of your JSON. If I use one of the tools from How to auto-generate a C# class file from a JSON object string to auto-generate a data model from your JSON, I get:
public class RootObject
{
public int id { get; set; }
public int id_user { get; set; }
public string name { get; set; }
public int id_user1 { get; set; }
public string name1 { get; set; }
}
This looks nothing like your Customer class.
The secondary reason that PopulateObject() fails is that Json.NET will only populate public members by default -- and yours are all private.
To fix this, I might suggest skipping the DataTable and json_string representations entirely, and building your list directly from the IDataReader interface that MySqlDataReader implements:
var lista = x
.SelectRows(r =>
// Extract the row data by name into a flat object
new
{
id = Convert.ToInt32(r["id"], NumberFormatInfo.InvariantInfo),
id_user = Convert.ToInt32(r["id_user"], NumberFormatInfo.InvariantInfo),
name = r["name"].ToString(),
id_user1 = Convert.ToInt32(r["id_user1"], NumberFormatInfo.InvariantInfo),
name1 = r["name1"].ToString(),
})
.Select(r =>
// Convert the flat object to a `Customer`.
new Customer
{
id = r.id,
user1 = new User { Id = r.id_user, Name = r.name },
user2 = new User { Id = r.id_user1, Name = r.name1 },
})
.ToList();
Using the extension method:
public static class DataReaderExtensions
{
// Adapted from this answer https://stackoverflow.com/a/1202973
// To https://stackoverflow.com/questions/1202935/convert-rows-from-a-data-reader-into-typed-results
// By https://stackoverflow.com/users/3043/joel-coehoorn
public static IEnumerable<T> SelectRows<T>(this IDataReader reader, Func<IDataRecord, T> select)
{
while (reader.Read())
{
yield return select(reader);
}
}
}
This assumes that your Customer data model now looks like:
public class Customer
{
public Int32 id { get; set; }
public User user1 { get; set; }
public User user2 { get; set; }
}
public class User
{
public Int32 Id { get; set; }
public string Name { get; set; }
}
You could also combine the SelectRows and Select calls into a single method; I separated them for clarity. Skipping the DataTable and JSON representations should be simpler and more performant than your current approach.
Demo fiddle using a mockup data reader here.

How to create a dictionary of field values/counts from an observable collection?

I have an ObservableCollection<CustomerModel> Customers, that holds a Country field. What I want to do is, create an observable collection of type PiePointModel. In order to store the country name and number of occurrences of that country name.
So I set up an ObservableCollection<PiePointModel> CountryRatioCollection, where PiePoint holds a name and amount.
Then I tried to assign that collection to my Customers, by converting it to a dictionary holding the required values:
CountryRatioCollection = new ObservableCollection<PiePointModel>();
CountryRatioCollection = Customers.GroupBy(i => i.Country).ToDictionary(g => g.Key, g => g.Count());
But I get an error stating that this can't be implicitly converted:
Error 2 Cannot implicitly convert type 'System.Collections.Generic.Dictionary<string,int>' to 'System.Collections.ObjectModel.ObservableCollection<MongoDBApp.Models.PiePointModel>'
I understand that this is because the Dictionary type is not the same as my PiePoint model class.
Can anyone offer advice on making query and conversion?
This is the PiePoint class for reference, that holds the name and amount:
public class PiePointModel
{
public string Name { get; set; }
public int Amount { get; set; }
}
And this is the CustomerModel that holds the country field:
public class CustomerModel
{
[BsonId]
public ObjectId Id { get; set; }
[BsonElement("firstName")]
public string FirstName { get; set; }
[BsonElement("lastName")]
public string LastName { get; set; }
[BsonElement("email")]
public string Email { get; set; }
[BsonElement("address")]
public string Address { get; set; }
[BsonElement("country")]
public string Country { get; set; }
public override string ToString()
{
return Country;
}
}
You should use Select (not ToDictionary) and create PiePointModel for each group.
IEnumerable<PiePointModel> piePoints = Customers.GroupBy(i => i.Country).Select(s => new PiePointModel()
{
Name = s.Key,
Amount = s.Count()
});
CountryRatioCollection = new ObservableCollection<PiePointModel>(piePoints);
Also notice that I used: CountryRatioCollection = new ObservableCollection<PiePointModel>(..) because CountryRatioCollection is of type ObservableCollection and you cannot assign here dictionary like in your example.
Constructor of ObservableCollection<T> can take IEnumerable<T> - I used it here.
Other way is use loop and add new PiePointModel to collection
CountryRatioCollection = new ObservableCollection<PiePointModel>();
var groups = Customers.GroupBy(i => i.Country);
foreach(var gr in groups)
{
PiePointModel piePointModel = new PiePointModel()
{
Name = gr.Key,
Amount = gr.Count()
};
CountryRatioCollection.Add(piePointModel);
}

Populating a List<class> from a query, results from List is address of the class

I have a class that I made and I want to use it for a list but when I try adding data to it and loop through the list all I'm getting is the address of the class. I have no idea why its added that as the values.
My Class is
public class Regions
{
public int dataID { get; set; }
public int documentID { get; set; }
public string region_ID { get; set; }
public string region_Name { get; set; }
public string sortID { get; set; }
}
This is how I am trying to add the values. The query has the right data in it, but the list isn't getting the data, just where the class resides.
List<Regions> lst = new List<Regions>();
var q = dt.AsEnumerable()
.Select(region => new {
dataID = region.Field<int>("dataID"),
documentID = region.Field<int>("documentID"),
region_ID = region.Field<string>("Region_ID"),
region_Name = region.Field<string>("Region_Name"),
sortID = region.Field<string>("SortID").ToString()
});
foreach (var d in q)
lst.Add(new Regions() { dataID = d.dataID,
documentID = d.documentID,
region_ID = d.region_ID,
region_Name = d.region_Name,
sortID = d.sortID
});
EDIT
Here is a link that I found that is similar what I am trying to do, but it doesn't seem he had the same errors as I did. I tried that answer, but wasn't working for me.
Storing data into list with class
Fixed
When I was looping through the list, I wasn't reading it properly.
Try adding this in Regions
public override string ToString()
{
return region_Name;
}
actually you can create the list in one step
List<Regions> lst = dt.Rows.OfType<DataRow>().Select(region => new Regions{
dataID = region.Field<int>("dataID"),
documentID = region.Field<int>("documentID"),
region_ID = region.Field<string>("Region_ID"),
region_Name = region.Field<string>("Region_Name"),
sortID = region.Field<string>("SortID")
}).ToList();

Categories