Use LINQ to refactor a WPF, grid.Children iterating method - linq

My aim is to convert this foreach logic to a single LINQ query.
foreach (var anyItem in gridReference.Children)
{
var enumerableCheck = anyItem as ListBox;
if (enumerableCheck != null)
{
foreach (var collectionItem in enumerableCheck.Items)
{
//check collectionItem is of type T
}
}
else
{
//check anyItem is of type T
}
}
I tried my hand at learning linq with this refactoring but the syntax errors are real off putting since im so used to C#. Also I do not know how to assign a working variable in linq which leads to constant casting.
Attempt:
IEnumerable <T> genericQuery = from UIElement element1 in gridReference.Children
where (element1 as ListBox) != null
from item in (element1 as ListBox).Items
where (item as T) != null
select (item as T)
from UIElement element2 in gridReference.Children
where element2 as T != null
select element2;

I think it's perfect example to use let clause in linq, to check that casting.
https://msdn.microsoft.com/en-us/library/bb383976.aspx
To make sure that you are returning only items that fits to T, you can use OfType<T>
List<T> result = (from element1 in gridReference.Children
let el1ListBox = element1 as ListBox
where el1ListBox != null
select el1ListBox.Items.OfType<T>()).ToList();
EDIT:
According to comment:
List<T> result = (from element1 in gridReference.Children.OfType<ListBox>().ToList()
select element1.Items.OfType<T>()).ToList();

I tested a working query thanks to madOX's encouraging reply.
IEnumerable <T> genericQuery = (from element1 in gridReference.Children.OfType <ListBox>()
from boxItem in element1.Items.OfType <T>()
select boxItem)
.Concat (from element2 in gridReference.Children.OfType <T>()
select element2);

Related

Escape null values LINQ

I have an issue with this linq expression:
var invs = ids.Split(new[] {'|'}, StringSplitOptions.RemoveEmptyEntries)
.Select(x => sitecoreContext.GetItem<Inv>(new ID(x).Guid))
.ToList();
How can I check for null into the .Select? SitecoreContext.GetItem(new ID(x).Guid)) to crash (because items being unpublished, or created but not published) so I need a way to verify first if the item exist, and only then to make the select.
Thank you.
When you call SitecoreContext.GetItem<T>, in the background SitecoreContext gets the item from the database and then it casts it to the T type. And from what I can see, it can throw an exception if there is no item with the specified ID.
What you can do to avoid this exception is split what SitecoreContext does and execute it on your own with a null check in between:
Execute GetItem first
Do the null check
Cast the item to your type:
var invs = ids.Split(new[] {'|'}, StringSplitOptions.RemoveEmptyEntries)
.Select(x => sitecoreContext.Database.GetItem(new ID(x)))
.Where(x => x != null)
.Select(x => sitecoreContext.Cast<Inv>(x))
.ToList();
You can filter all non-null items using a where statement.
var nonNull = list.Where(element => element != null);
I usually use an extension method for this:
public static class EnumerableExtensions
{
public static IEnumerable<T> WhereNotNull<T>(this IEnumerable<T> enumerable)
where T: class
{
return enumerable.Where(element => element != null);
}
}
Given your example, you could use the statement like this:
var invs = ids.Split(new[] {'|'}, StringSplitOptions.RemoveEmptyEntries)
.WhereNotNull()
.Select(x => sitecoreContext.GetItem<Inv>(new ID(x).Guid))
.ToList();
You Can Check Null Using null coalescing operator or ternary Operator in Linq Example is Given Below
`var productTypes = from ProductDto e in Product
select new
{
Id = e.Product != null ? e.Product.ID : 0,
Name = "xyz"
};`

Dictionary<string, List<Dictionary<string,string>>> query using linq

I have the below dictionary
Dictionary<string,string> q1=new Dictionary<string,string>
{
{"h1","name1"},{"h2","name2"}
};
Dictionary<string,string> q2=new Dictionary<string,string>
{
{"h1","name12"},{"h2","name23"}
};
Dictionary<string,string> q3=new Dictionary<string,string>
{
{"h1","name123"},{"h2","name234"}
};
List<Dictionary<string,string>> m1 = new List<Dictionary<string,string>> { q1,q2,q3 };
Dictionary<string, List<Dictionary<string,string>>> mhi = new Dictionary<string, List<Dictionary<string,string>>>();
mhi.Add("x1", m1);
I need to return a list which has the values name1,name12,name123 using linq.
I am aware of normal method which works for me. But I am curious to know how to implement this using linq
Try this:
var q1 = new Dictionary<string, string> {
{"h1", "name1"},
{"h2", "name2"}
};
var q2 = new Dictionary<string, string> {
{"h1", "name12"},
{"h2", "name23"}
};
var q3 = new Dictionary<string, string> {
{"h1", "name123"},
{"h2", "name234"}
};
var m1 = new List<Dictionary<string, string>> { q1, q2, q3 };
//Using LINQ
List<string> result = (from dictionary in m1
from keyValuePair in dictionary
where keyValuePair.Key == "h1"
select keyValuePair.Value).ToList();
//result = name1,name12,name123
//without linq
var result2 = new List<string>();
foreach(var dictionary in m1)
foreach(var keyValuePair in dictionary)
if(keyValuePair.Key == "h1")
result2.Add(keyValuePair.Value);
Edit:
The from clause specifies the data source, the where clause applies the filter, and the select clause projects each element of the sequence into a new form.
Linq queries are not executed until we iterate through it. (Here .ToList() is doing that). It's like a blueprint which specifies how the information is returned when executed(iterated).
Lets examine each statement separately:
from dictionary in m1 - This is much like foreach(var dictionary in m) except that it doesn't iterate (Because its a blueprint). It specifies which source we are iterating through (m1) and the variable to assign to each member (dictionary that is. We know that it will be of type Dictionary<String, String>)
from keyValuePair in dictionary - Here we use the dictionary variable created from the previous statement. The type of keyValuePair will be KeyValuePair<string,string> because we will be "iterating" through a Dictionary<string,string> when the query is executed.
where keyvaluePair.Key == "h1" - This filters out the keyValuePairs from the previous statement whose Key property equals "h1".
Now that we filtered out the KeyValuePairs, we can select their Value property. This "projects" the filtered out KeyValuePair sequence to the new type IEnumerable<string>
Finally, ToList method executes the query to get the results.

LINQ Object Referance not set

I am getting an "Object Reference not set to an instance of an object" error when searching for an item (on Guid) that is selected from a datagrid. I have checked that the item does return the Guid correctly (by writing it to a label on the page), however in my linq query (i assume) i am comparing incorrectly.
ctx is the domaindatasource, I know the element im trying to remove exists.
private void medItemRemove_Click(object sender, RoutedEventArgs e)
{
MedicineInventory M = (MedicineInventory)medicineInventoryDataGrid.SelectedItem;
Guid Mid = M.MedicineInventoryId;
MedicineInventory toRemove = new MedicineInventory();
toRemove = (from a in ctx.MedicineInventories where (a.MedicineInventoryId == Mid) select a).Single();
ctx.MedicineInventories.Remove(toRemove);
ctx.SubmitChanges();
}
Rewrite your code as follows:
private void medItemRemove_Click(object sender, RoutedEventArgs e)
{
MedicineInventory M = (MedicineInventory)medicineInventoryDataGrid.SelectedItem;
Guid Mid = M.MedicineInventoryId;
MedicineInventory toRemove = (from a in ctx.MedicineInventories where (a != null && a.MedicineInventoryId == Mid) select a).SingleOrDefault();
if (toRemove != null){
ctx.MedicineInventories.Remove(toRemove);
ctx.SubmitChanges();
}
else { .... } // code if toRemove is null
}
Is a null at any point?
toRemove = (from a in ctx.MedicineInventories where (a != null && a.MedicineInventoryId == Mid) select a).Single();
I think your problem is happening because you're creating a new MedicineInventory.
Replace this:
MedicineInventory toRemove = new MedicineInventory();
With this:
var toRemove = ctx.MedicineInventories.Single(mi => mi.MedicineInventoryId == Mid);
Update:
When it retuns the error message "Sequence contains no elements" it's because EF could not find a row in the database with the same Guid you're using in the where clause. In this case and to avoid an exception, you can try this line of code:
var toRemove = ctx.MedicineInventories.SingleOrDefault(
mi => mi.MedicineInventoryId == Mid);
then use an if to delete if it's not NULL:
if(toRemove != null)
{
ctx.MedicineInventories.Remove(toRemove);
ctx.SubmitChanges();
}
else
{
// Only you know what to do! :-)
}
SingleOrDefault returns the only element of a sequence, or a default
value (NULL in this case) if the sequence is empty; this method
throws an exception if there is more than one element in the sequence.
Note: the way you're comparing the Guids is correct because == is overloaded on Guid so you don't need to compare the string representations.
See http://msdn.microsoft.com/en-us/library/system.guid.op_equality%28v=vs.110%29.aspx#Y474

Linq to dataset select row based on max value of column

I have a dataset table, I want to group it by column MOID, and then within this group I want to select the row which has max value of column radi.
Can anybody show me how to do it via LINQ to dataset?
Although the solution posted by Barry should work (with a few fixes), it is sub-optimal : you don't need to sort a collection to find the item with the maximum value of a field. I wrote a WithMax extension method, which returns the item with the maximum value of the specified function :
public static T WithMax<T, TValue>(this IEnumerable<T> source, Func<T, TValue> selector)
{
var max = default(TValue);
var withMax = default(T);
bool first = true;
var comparer = Comparer<TValue>.Default;
foreach (var item in source)
{
var value = selector(item);
int compare = comparer.Compare(value, max);
if (compare > 0 || first)
{
max = value;
withMax = item;
}
first = false;
}
return withMax;
}
It iterates the collection only once, which is much faster than sorting it just to get the first item.
You can then use it as follows
var query =
from row in table.AsEnumerable()
group row by row.Field<int>("MOID") into g
select g.WithMax(r => r.Field<int>("radi"));
This is untested but I think something like this should work:
var qry = from m in [YourDataSource]
group p by m.MOID into grp
select grp.OrderByDescending(a => a.RADI).First();
this works with one query!
public static T WithMax<T, TValue>(this IEnumerable<T> source, Func<T, TValue> keySelector)
{
return source.OrderByDescending(keySelector).FirstOrDefault();
}

How Can I Get the Identity Column Value Associated with a SubSonic 3 LinqTemplate Insert?

I am using SubSonic 3.0.0.3 along with the Linq T4 Templates. My ProjectRepository, for example, has the following two methods:
public int Add(Project item)
{
int result = 0;
ISqlQuery query = BuildInsertQuery(item);
if (query != null)
{
result = query.Execute();
}
return result;
}
private ISqlQuery BuildInsertQuery(Project item)
{
ITable tbl = FindTableByClassName();
Insert query = null;
if (tbl != null)
{
Dictionary<string, object> hashed = item.ToDictionary();
query = new Insert(_db.Provider).Into<Project>(tbl);
foreach (string key in hashed.Keys)
{
IColumn col = tbl.GetColumn(key);
if (col != null)
{
if (!col.AutoIncrement)
{
query.Value(key, hashed[key]);
}
}
}
}
return query;
}
Along with performing the insert (which works great), I'd really like to get the value of the auto-incrementing ProjectId column. For the record, this column is both the primary key and identity column. Is there perhaps a way to append "SELECT SCOPE_IDENTITY();" to the query or maybe there's an entirely different approach which I should try?
You can do this with the ActiveRecord templates which does all of the wiring above for you (and also has built-in testing). In your scenario, the Add method would have one line: Project.Add() and it would return the new id.
For your needs, you can try this:
var cmd=query.GetCommand();
cmd.CommandSql+=";SELECT SCOPE_IDENTITY() as newid";
var newID=query.Provider.ExecuteScalar(cmd);
That should work..
*Edit - you can create an ExtensionMethod for this on ISqlQuery too, to save some writing...

Resources