Does Linq OrderBy Not Sort Original Collection? - c#

I am using Linq To Sql as my database layer for the first time and I have run into an issue. Basically, I have a form that allows users to create predefined jobs. A predefined job can have many predefined job items. For example, a predefined job might be something like an oil change. The predefined job items would be oil, labor, etc. In my database PredefinedJobs is a table and PredefinedJobItems is another table with a foreign key back to PredefinedJobs. I have a form for adding predefined jobs that has the Linq-to-Sql class backing the form as a class variable. There is a ListView on the form that displays all of the jobs items. A new feature has required me to track the position of an item in the ListView. For example, if my item ListView looks like below, note the order:
Qty Name Desc ItemOrder
4 Oil Penzoil 0
1 Labor 1
Because the items are added via a child form I do not want to provide access to the ListView. So, I created the method below in an attempt to both create the ItemOrder and sort the collection on the PredefinedJob Linq to Sql object. It does not appear that the OrderBy function on the List actually sorts the collection on the PredefinedJob. What would be the best way to maintain order on the Linq to Sql collection (i.e. PredefinedJob.fkJobItems)? Or, would it be better to just pass a reference to my ListView into the child form that adds the items to the jobs where I have access to the selectedIndex?
private SortAndOrderItems(List<PredefinedJobsItem> items)
{
var nextItemOrderNumber = items.Max(max => max.ItemOrder) + 1;
foreach (var item in items)
{
if (item.ItemOrder == null)
{
item.ItemOrder = nextItemOrderNumber;
nextItemOrderNumber++;
}
}
items.OrderBy(i => i.ItemOrder).ToList();
}

OrderBy creates a new query, that will, when executed, not alter your original list.
Why not just the Sort method of the List?
items.Sort((a, b) => a.ItemOrder.CompareTo(b.ItemOrder));

I think you were looking for List<>.Sort
class Cmp : IComparer<PredefinedJobsItem>
{
public int Compare(PredefinedJobsItem x, PredefinedJobsItem y)
{
return x.ItemOrder.CompareTo(y.ItemOrder);
}
}
var comparison = new Cmp();
items.Sort(comparison);

Related

LINQ Query - exclude the values of one list from another list

I need some help with a LINQ query please.
Here's my code :
private void ReturnSensitivitiesSearchResults()
{
PatientRecord PMR = new PatientRecord();
// Return the list of sensitivities (Ingredient objects), but do not include any where the "SuitableForSensitivityChecking" is false - as we wouldn't want to include ingredients in the list where
// they can't be screened for sensitivities. - This returns an array of Ingredient[]
var ListOfSensitivities = FDBSystem.Navigation.GetIngredientsByName(txtSearchText.Text + "*", sensitivityType).Where(n => n.SuitableForSensitivityChecking != false);
// Return a list of all of the Sensitivities logged on a PatientRecord (which is a list of Ingredient objects)
var PatientSensitivities = PMR.Sensitivities;
// Populate the drug information into the grid control.
this.dgvSearchResults.DataSource = ListOfSensitivities.ToArray();
}
class PatientRecord
{
public List<Ingredient> Sensitivities = new List<Ingredient>();
}
What I need is a LINQ statement that returns the list of sensitivities, but not including the ones that are in the PatientRecord Sensitivities list.
What I'm trying to do is list all sensitivities, in a datagridview control.... the user can then drag and drop one of these onto a treeview control which adds it to the PatientRecord Sensitivities list. I'm then wanting the datagridview to refresh with the sensitivites minus the ones already in the Patient Record, so that the same sensitivity can't be added to the treeview twice.
Hope you can help.
Assuming PatientSensitivities and ListOfSensitivities are actually a List and not some custom type, I think an Except() will do what you're looking for.
this.dgvSearchResults.DataSource = ListOfSensitivities.Except(PatientSensitivities).ToArray();

how to control the order of the Childs added and submitted to a parent in Linq to sql in C#?

so the problem is i have been coding on a project,in this project a bill of sale which is table A has 3 products which are represented by table B in our concept.
logic states that when i add product 1,2 and 3 to the object that represents table A,they should be inserted as added,i.e it should be as :
Table A Children =>
1,
2,
3
however in some of our forms in the project it works differnetly,somtimes i see the children are submitted as 3 1 2,or 2 1 3....
as i stated,this is a big project that has forms that are over 9k code so sadly i cant just copy some of the code for you guys...
however im despirately searching for methods which control the order in which the items are submitted.
the project is an accounting program and the products order in a bill of sale matters...
in short this is whats happening
Sales A = new A();
Foreach (var row in Datagridview.Rows)
{
//Note That the products in the datagrid are listed as 1,2,3
A.Product = new Product{name = row.Cells[name.index].value.ToString()}
}
dc.Sales.InsertOnSubmit(A);
dc.SubmitChanges();
//but its submitted out of order
You don't control the arbitrary order in which data is stored. You do control the order in which it's sorted when you query and display that data.
You can order records with the .OrderBy() extension method. Something like this:
var salesRecord = db.Sales.Single(s => s.Id == someId);
foreach (var product in salesRecord.Products.OrderBy(p => p.SomeValue))
DisplayProductToUser(product);
Any IEnumerable<T> can be sorted similarly:
var orderedSales = db.Sales.OrderBy(s => s.SomeValue);

Referring to an attached table's fields in Linq-to-SQL

I'm creating a windows forms application to display all the data in my database.
So the database has a table called Items, a sub-table called Tags, and these two tables have a 1:M relationship. I can add a Linq-to-SQL class to my Project, then create a datagrid to access the main Items table without any problems.
Looking at the properties of the Items class that was created by Linq-to-SQL, I can see it has the Tags table attached to it (EntitySet), but I can't access any of the Tags fields! Is there some way to do this?
I want to create a new column in the datagrid, called TagsList, and for each Item, I want to display a concatenated list of all corresponding Tags.Name. I figured I could do something like adding a new property to the Items class as so:
public partial class Items
{
public string TagsList
{
return this.Tags.Name.Aggregate((x, y) => x + ", " + y));
}
}
But obviously, this isn't working, because there is no definition for it. Help?
I am assuming that Items.Tags is of type ICollection<Tags> (or IQueryable or IEnumerable, I can't remember which linq-to-sql uses) as you state that Items and Tags have a one-to-many relationship. If this is not the case let me know and I'll delete or edit my answer as appropriate.
Name is a property on each individual Tags and not the collection which is why Tags.Name is incorrect.
Try the following:
return this.Tags.Select(tag => tag.Name).Aggregate((x, y) => x + ", " + y));
Tags.Select(tag => tag.Name) will return IEnumerable<string> of names and then you aggregate that.

Aggregate List Based on Child Properties

I have an class structure representing an item's list of materials, with each material having a list of additional properties. Similar to the following:
MaterialList
MaterialPiece
AdditionalPropertyList
AdditionalProperties
Each class has a ChildPieces list property containing the list of items in the next level; for example, the MaterialList class has
public List<MaterialPiece> ChildPieces {get;set;}
So an example instance of this might be
WheelSet
Wheel
Wheel Properties
Quantity: 1
Partnumber: R1000
Wheel
Wheel Properties
Quantity: 1
Partnumber: R1000
What I want to do is aggregate the MaterialList to group together the MaterialPiece objects based on some of their properties - the Partnumber in the above example.
This would result in
WheelSet
Wheel
Wheel Properties
Quantity: 2
Partnumber: R1000
I want to do this in the outermost object, i.e. I want to implement
class MaterialList : BasePiece
{
public void AggregateMaterialPieces()
{
var newList = ChildPieces.Where(...)
}
}
So my question: Can I use LINQ to group and sum the MaterialPieces in the MaterialList based on some known values in the AdditionalPropertyList?
Your question isn't quite specific enough but anyway, here's some code that will give you the quantities for each partnumber.
var groupings = list.SelectMany(x => x.AdditionalPropertyList).GroupBy(x => x.PartNumber).Select(g => new { PartNumber=g.Key, Quantity=g.Sum(x => x.Quantity) } );
foreach (var g in groupings)
Console.WriteLine("PartNumer: {0} Total: {1}", g.PartNumber, g.Quantity);
I'm not positive this is correct, if it doesn't work let me know and I'll try to make time to actually test it. The basic idea is to flatten the list to all the additional properties, then you group those by partnumber, then for each of the groupings you create a new anonymous object which uses the partnumber (the group key) and calls aggregate on the list of groupings to get the sum of all the quantities.

Filtering a ObservableCollection by user input

I have an ObservableCollection of about 1000 objects that needs to be filtered (searched) by the end user. The user must be able to search by name or employee id. The List Control consumes FilteredEmployees and Employees is loaded up with everything on page load.
I currently have it set up as such:
public ObservableCollection<EmployeeServicesData> Employees { get; set; }
public ObservableCollection<EmployeeServicesData> FilteredEmployees { get; set; }
internal void FilterEmployee(string searchText, bool isByName)
{
if (searchText.Length > 0)
{
IEnumerabe<EmployeeServicesData> filter;
if (isByName)
filter = Employees.Where(x => x.Name.Length >= searchText.Length).Where(x => x.Name.Substring(0, searchText.Length) == searchText.ToUpper());
else
filter = Employees.Where(x => x.EmployeeNumber.ToString().Length > searchText.Length).Where(x => x.EmployeeNumber.ToString().Substring(0, searchText.Length) == text);
foreach (EmployeeServicesData employee in filter)
FilteredEmployees.Add(employee);
}
}
Sanitation is handled before this method.
This doesn't smell very efficent. Should I use two methods for this, or is there a better way to handle filtering?
I'd like to keep Employees at an unchanged state so I can repopulate FilteredEmployees to the full list without hitting the DB again.
I know this is an old post but I was using it to help me with the filtering aspect and noticed that SlipFish was creating the ObservableCollection by looping round the IEnumerable collection.
As the ObservableCollection constructor accepts an IEnumerable collection the ObservableCollection could be created like this:
FilteredEmployees = new ObservableCollection<EmployeeServicesData>(filter);
It looks like you're trying to see if searchText is contained in the Employee name, or in the Employee Number.
You could do this instead:
x.Name.IndexOf(searchText, StringComparison.OrdinalIgnoreCase) >= 0
x.EmployeeNumber.ToString().IndexOf(searchText, StringComparison.OrdinalIgnoreCase) >= 0
Or you could use StartsWith instead of IndexOf.
Edit: Another problem with List Controls with large amounts of data in them is that it takes a long time to render. So if you have it unfiltered when you start and Silverlight or WCF or whatever has to render all 1000 into the control even though you don't see all of them, it can take a little bit of time. Silverlight 3 has UI Virtualization, which would probably be the best optimization you could do here.
Take a look at this post for a filtered observable collection.
You can this through the PagedCollectionView.
Check out this blog post for an example: http://blogs.msdn.com/b/avip/archive/2009/10/30/real-time-list-filtering-with-silverlight-mvvm-and-pagedcollectionview.aspx

Categories