How to get a recursive selection in c#? - c#

I'm retrieving this selection from database into a datable in c#.
DataTable cataloguess = catalogue.CatalogueChirMedTech(id);
i have Categorie object, I can have under Categorie other Categorie objects(List) and under it. I can have also other Categories objects and etc(recursive).
So in c#, I want to create each Categorie with its list of categories if exists, and its modeles.Modele are rows with estterminal='o'.
Rows with pereid=-256 are parent and their children are rows with pereid is the id of the parent (typeid).
public class Categorie {
private List<Categorie> sousCategoriesField;
private List<Modele> modelesField;
public List<Categorie> Categories {
get {
return this.sousCategoriesField;
}
set {
this.sousCategoriesField = value;
}
}
public List<typeModele> modeles {
get {
return this.modelesField;
}
set {
this.modelesField = value;
}
}
}
Thanks.

First I suspect your Categorie object is going to need an identifier of some kind. Something as simple as:
public int ID { get; set; }
The recursive part should be simple enough. Logically a method might look something like this:
private List<Categorie> GetCategories(int ID, DataTable sourceData)
{
var result = GetCategoriesFromData(ID, sourceData);
foreach (var categorie in result)
categorie.Categories = GetCategories(categorie.ID, sourceData);
return result;
}
You'd need to expand on the placeholder GetCategoriesFromData in here, of course. That's really up to you and outside the scope of the recursion itself. Basically you'd query the DataTable for rows which have the given ID as their parent, convert those rows to Categorie objects (populate the ID and the modeles, whatever those are), and return that list.
But once you have this structure, the recursion is simple. All you need to do is call it with your initial "magic number" for the top-level parent "categories":
DataTable cataloguess = catalogue.CatalogueChirMedTech(id);
var categories = GetCategories(-256, cataloguess);
Then the GetCategories method will recursively populate the child categories, passing along the ID of each "parent" at each level of recursion.

Related

Can't change value in foreach from IEnumerable<Model>

How to change value of an object in foreach from IEnumerable<Model>.
Code:
public IEnumerable<Model> ListDaftarPjsp()
{
IEnumerable<Model> list = from x in db.PJSPEvaluation
select new Model
{
foo = x.Foo,
bar = x.Bar
};
foreach (Model item in list) {
item.condition = "example";
}
return list;
}
public class Model{
public string foo{ get; set; }
public string bar { get; set; }
public string condition{ get; set; }
}
I already create Model. Then I am looping result using foreach, then set it. But the Return for conditionstill not changing? how to set condition inside foreach then return it for result
IEnumerable<T> is a query, not a collection. While there is some sort of collection at the other end, the query itself is not the collection. The nature of the collection you are targeting will determine whether or not you can modify the contents.
The general rule of thumb is that you can't expect an IEnumerable<T> to return the same list of objects twice, or even expect that you will be able to enumerate across it more than once - it is perfectly valid (if unusual) for an IEnumerable<T> to enumerate once only and refuse to enumerate a second or third time.
In this case what you have is actually a database query of type IQueryable<Model> that is cast to IEnumerable<Model>. It's still an IQueryable<Model> which means that each time you enumerate across it you will get (probably) the same list of data but in completely new objects. Changing one of the objects won't change all of the objects for the same source record, nor change the contents of the underlying record itself.
If you are trying to modify the returned objects without changing the underlying records (seems to be the case) then you need to materialize the query into a collection in memory. There are a few ways to do this depending on what you're expecting to do with the returned data.
The simplest is to convert the query to an array using the .ToArray() extension method:
public Model[] ListDaftarPjsp()
{
var query = from x in db.PJSPEvaluation
select new Model
{
foo = x.Foo,
bar = x.Bar
};
var list = query.ToArray();
foreach (Model item in list)
{
item.condition = "example";
}
return list;
}
Now the records are in an array in memory and enumeration of that array can be done multiple times returning the same exact objects instead of fetching new copies of the data from the database every time.
Here you are trying to create a list of Model using LINQ, then you are iterating the same for adding an additional property to each item. Then why don't you add the property at the time of creation of the list instead for an additional loop? Make things simple by try something like this:
from x in db.PJSPEvaluation
select new Model
{
foo = x.Foo,
bar = x.Bar,
condition = GetCondition(x.Foo)
};
Where the GetCondition() can be defined as :
private string GetCondition(int foo)
{
if(item.foo == 1)
{
return "a";
}
else if(item.foo == 2)
{
return "b";
}
else
{
return "xx";
}
}
There is already for this topic but there is more efficient way to do this.
Just use List<> instead of Array[].
public List<Model> ListDaftarPjsp()
{
List<Model> list = from x in db.PJSPEvaluation
select new Model
{
foo = x.Foo,
bar = x.Bar
};
foreach (Model item in list)
{
item.condition = "example";
}
return list;
}
public class Model{
public string foo{ get; set; }
public string bar { get; set; }
public string condition{ get; set; }
}
I case you dont want to load items in memory with a .ToArray or .ToList
You can use .Select from Linq.
return myEnumeration.Select(item => {
item.condition = "example";
return item;
})

when i loop though tuple list it show me index out of range error

my kitchen list format are this List<Tuple<string,string,string>> .I can filled it on kitchen class method and i can display it on datagridview by calling that method by kitchen class object when i apply foreach loop on it it show me index out of range exception
kitchen = new Kitchen();
List<Tuple<string, string, string>> kitchenList = kitchen.getKitchens();
dgv_kitchen.Rows.Clear();
if (kitchenList.Count > 0)
{
for (int i = 0; i < kitchenList.Count; i++)
{
dgv_kitchen.Rows[i].Cells["id"].Value = kitchenList[i].Item1.ToString();
dgv_kitchen.Rows[i].Cells["kitchen_name"].Value = kitchenList[i].Item2.ToString();
dgv_kitchen.Rows[i].Cells["categories"].Value = kitchenList[i].Item3.ToString();
}
}
You're clearing the list of rows - but then trying to access those non-existent rows by index. Contrary to your title, it's not "looping through a tuple list" that's causing the problem - it's what you're doing with the results.
Instead, you should be creating new rows and adding them. Note that this is easier with a foreach loop than a for loop, and you don't need to check the count first either way:
kitchen = new Kitchen();
List<Tuple<string, string, string>> kitchenList = kitchen.getKitchens();
dgv_kitchen.Rows.Clear();
foreach (var tuple in kitchenList)
{
dgv_kitchen.Rows.Add(new object[] { tuple.Item1, tuple.Item2, tuple.Item3 };
}
That's assuming the DataGridView displays just id/kitchen_name/categories in that order.
A better solution would be to avoid setting the cell values directly, and instead bind the DataGridView to a data source. I'd also avoid the tuples. So you might have:
public class Kitchen
{
public string Id { get; }
public string Name { get; }
public string Categories { get; }
public Kitchen(string id, string name, string categories)
{
Id = id;
Name = name;
Categories categories;
}
}
You'd then change your current getKitchens method to something like:
public IReadOnlyList<Kitchen> GetKitchens()
Then you can replace all of the existing code with:
dgv_kitchens.DataSource = GetKitchens();

Searching data in datagridview

I have a datagridview that displays data from a database table. There are two columns ID and NAME.
I have a textbox in which i enter name and data of those names appear in datagridview. I have achieved searching of data but i want to search as done in comboBox. When i type "a" then all names starting with "a" should appear in datagridview. Then if i type "arn" then all names starting with "arn" should appear in datagridview. I need to know if there is a built-in method or some LOGIC that i should consider.
I am using Linq to Sql.
EDIT-1
I have made a subclass in the form class
public class Table1
{
public int ID;
public string Name;
public MyList(string _newID, string _newName)
{
_id = _newID;
_name = _newName;
}
public int _id
{
get { return ID; }
set { ID = value; }
}
public string _name
{
get { return Name; }
set { Name = value; }
}
}
Used BindingList to bind it with dataGridView1. I have applied textChanged event on a textBox1.
BindingList<Table1> tempData = new BindingList<Table1>();
string name = textBox1.Text;
var result = from row in context.Tables
where row.Name == name
select row;
foreach (Table std in result)
{
tempData.Add(new MyList(row.ID, row.Name);
}
dataGridView1.DataSource = tempData;
This is how i am doing but this searches for exact same name. I want to make it like comboBox drop down search. In which at each key typed the search result gives names/strings containing those characters.
Thanks.
Try this
Entity Framework wildcards & Linq
Like operator or using wildcards in LINQ to Entities
http://www.codeproject.com/Articles/634104/Csharp-Wildcard-Search-Using-LINQ

Joining two datatables as parent-child in linq

I need to build a typed list of parent-child objects that are read from two different Excel sources: One describes parent object, another describes child objects. The hierarchy is only 2 layers ever.
Reading into excel is not the issue, as it is read into 2 untyped datatables, but joining the information is.
The structure is very plain:
Parent has an ID and some text fields
Children have a parentID (so its 1-m) and some text fields
The objects that these are to be populated into looks like this:
public class ParkingSites
{
public List<ParkingLot> Lots { get; set; }
public ParkingSites(List<ParkingLot> arg)
{
Lots = arg;
}
}
public class ParkingLot
{
public List<Bay> Bays{ get; set; }
public int Id { get; set; }
public List<string> ParkingLotDetails { get; set; }
public ParkingLot()
{
}
}
public class Bay
{
public List<string> BayDetails { get; set; }
public int ParentId { get; set; }
public Bay()
{
}
}
The excel sources have a fixed column order with the parent sheet's first column being the parentId, and the first column on the child sheet also being the parentId.
EDIT: After playing around a bit, I just made both parent and child classes typed, as the initial reason for leaving them mostly untyped lead to more problems than it prevented. This is part of a larger project where the untypedness is a better solution for our problem on the other classes with data that is not hierarchial.
You can simply group the list of children by the parent id, and then iterate over the parents and add each child that belongs to it.
For example, you could use ToLookup:
// assuming you have all Bay instances in a collection called bays
var baymap = bays.ToLookup(b => b.ParentId);
// and all ParkingLot instances in a collection called lots
foreach(var lot in lots)
lot.Bays.AddRange(baymap[lot.Id]);
or, using the first element in the details lists:
var baymap = bays.ToLookup(b => b.BayDetails[0]);
foreach(var lot in lots)
lot.Bays.AddRange(baymap[lot.ParkingLotDetails[0]]);
or, using Where without a lookup (probably slower, depends on your data):
foreach(var lot in lots)
lot.Bays.AddRange(bays.Where(b => b.ParentId == lot.Id));

C# DataGridView empty when using concrete type, displays data when using anonymous type

I'm trying to display using a DataGridView and I'm getting some strange results.
When I set the data source with an anonymous type like so:
var displayList = CreateAnAnonymousBindingList(new { prop1 = string. Empty ...etc... } );
displayList.AllowNew = true; //The property in the DataGridView is set in the designer
var list = from someEntity in entities.EntityGroup //I want some of the fields from each entity
select new { prop1 = someEntity.prop1...etc...};
foreach(item in list)
{
displayList.add(item);
}
form.dataGridView.DataSource = displayList;
The data I want is displayed, but I cannot add new items, there is an exception caused by the anonymous type. This, I know, is because it is an anonymous type and has no constructors.
The problem is, when I create a concrete class using the same types, even names as the anonymous type, create a BindingList (simply by new BindingList()) and add items to it like:
BindingList<ClassName> displayList = new BindingList<ClassName>();
displayList.AllowNew = true;
var list = from someEntity in entities.EntityGroup
select someEntity;
foreach(var item in list)
{
ClassName temp = new ClassName();
/* Assign all the properties I want*/
displayList.Add(temp);
}
form.dataGridView.DataSource = displayList;
Nothing is displayed, even though the list has items in it, and the data source is set to the list. I cannot work out why this is happening, maybe I'm overlooking something really, really simple, but I cannot see where the issue is coming from.
Any help would be fantastic.
Change your class to use properties and datagridview will display the list.
class ClassName
{
public string ID { get; set; }
public string Name { get; set; }
}

Categories