C#/LINQ: Trying to optimize performance - c#

This is my setup
class EditorTabViewModel : TabViewModel {
...
public bool CanSave { get; set; };
}
ObservableCollection<TabViewModel> _tabs
I want to check if there are any tabs in _tabs that are EditorTabViewModel that has property CanSave set to true
i did something like ...
var tabs = from t in _tabs
where t is EditorTabViewModel
&& ((EditorTabViewModel)t).CanSave == true
select t;
if (tabs.Count() > 0)
return true;
else
return false;
I wonder if there is a better way to do this? maybe i won't need to retrieve all tabs, or maybe I just need to query the count or something?

How about:
return _tabs.OfType<EditorTabViewModel>().Any(t => t.CanSave);
Here:
OfType<> is a non-buffering filter that restricts us to EditorTabViewModel
Any is short-circuiting, so returns true as soon as a match is found

Yes, you can improve. Something like this would probably work:
return _tabs.Any(x => x is EditorTabViewModel && ((EditorTabViewModel)x).CanSave);

Using the linq extensions you could write something like
_tabs.Any( p => p is EditorTabViewModel && ((EditorTabViewModel)t).CanSave)

Try something like:
return _tabs.FirstOrDefault(y => y is EditorTabViewModel && ((EditorViewModel)t).CanSave) != null;

Related

Avoid an inner .ToList() in a LINQ query?

I am building a method that takes a List<T> and turns it into a DataTabe. In the process, I want to filter out any properties that are tagged with the [NotMapped] attribute.
I have the entire method working, but I am a bit worried about one part of it... the part that weeds out the [NotMapped] properties. Here's what I've got:
public static DataTable CreateDataTable<T>(IEnumerable<T> list)
{
Type type = typeof(T);
var properties = type.GetProperties().Where(p =>
p.CustomAttributes.ToList().Count == 0 ||
(p.CustomAttributes.ToList().Count > 0 && p.CustomAttributes.ToList()[0].AttributeType.Name != "NotMappedAttribute")
).ToList();
// Rest of the method...
}
So, that works as I'd like it to and gets rid of anything that looks like this (for example) so it doesn't end up in the final DataTable:
[NotMapped]
public string Description { get; set; }
My concern is about performance and just general best practice. The var properties = LINQ query seems clumsy to me, but I'm not seeing a more efficient way to improve it.
Namely, I don't like calling p.CustomAttributes.ToList() 3 times. Is there a way to avoid this?
private IEnumerable<PropertyInfo> GetPropertiesWithoutAttribute<TAttribute>(Type type)
where TAttribute : Attribute
{
return type.GetProperties().Where(p => !p.GetCustomAttributes<TAttribute>().Any());
}
To answer the original question, you can avoid calling ToList() multiple times by saving its return value:
type.GetProperties().Where(p =>
p.CustomAttributes.ToList().Count == 0 ||
(p.CustomAttributes.ToList().Count > 0 && p.CustomAttributes.ToList()[0].AttributeType.Name != "NotMappedAttribute")
)
... becomes...
type.GetProperties().Where(p =>
{
var attrs = p.CustomAttributes.List();
return attrs.Count == 0 || (attrs.Count > 0 && attrs[0].AttributeType.Name != "NotMappedAttribute");
})
However, I recommend instead doing this:
type.GetProperties().Where(p => p.GetCustomAttribute<NotMappedAttribute>() == null))
Its shorter and easier to understand at a glance.

Construct expression for where through lambdas

The situation
I have a method that takes in a POCO. This POCO is like the following
private class SearchCriteria
{
public string name get;set;
public string age get;set;
public string talent get;set;
..
....
}
The method basically has a query to the db , that uses the above criteria.
public void query(SearchCriteria crit)
{
if(crit.name!=null && crit.age!=null && crit.talent !=null)
{
dbContext.Students.Where(c=>c.name ==crit.name && c.age==crit.age...)
}
else if(crit.name !=null && crit.age!=null)
{
}
else if(....
{
}
As you can see there is a definite problem above , where in, in case of large number of criteria, I will have to write a lot of if-elses to drop out specific arguments from the where clause .
The possible solution ?
I am actually new to the lambda expressions world but I believe we must be having a facility which would allow us to do something like below.
dbContext.Students.Where(processCriteria(searchCriteriaPOCO)).
Can you folks lead me to the proper direction ?. Thanks
Get a queryable and then keep adding where clauses to it. That way you only need to test each possible criteria the once and also only generate the number of where clauses that are absolutely needed.
IQueryable<Student> q = dbContext.Students.AsQueryable();
if (crit.name != null)
q = q.Where(c => c.name == crit.name);
if (crit.age != null)
q = q.Where(c => c.age== crit.age);
Let me start by saying that this answer uses the same basic idea as #PhilWright's answer. It just wraps it up in an extension method that applies this pattern for you, and allows you to have a syntax that reads nice.
public static class SearchExtensions
{
public static IQueryable<Student> Query(this SearchCriteria criteria, IQueryable<Student> studentQuery)
{
return studentQuery
.Match(criteria.name, (student) => student.name == criteria.name)
.Match(criteria.age, (student) => student.age == criteria.age)
.Match(criteria.talent, (student) => student.talent == criteria.talent);
// add expressions for other fields if needed.
}
private static IQueryable<Student> Match<T>(
this IQueryable<Student> studentQuery,
T criterionValue,
Expression<Func<Student, bool>> whereClause) where T : class
{
// only use the expression if the criterion value is non-null.
return criterionValue == null ? studentQuery : studentQuery.Where(whereClause);
}
}
You can then use it in your code like this:
var criteria = new SearchCriteria() {
name = "Alex",
talent = "Nosepicking"
};
var results = criteria.Query(dbContext.Students);
Maybe I'm missing something, as the code example is not the clearest I've seen, but for your specific example, I would think the following should be fine:
dbContext.Students.Where(c => (crit.name == null || crit.name == c.name) &&
(crit.age == null || crit.age == c.age) &&
(crit.talent == null || crit.talent == c.talent));
No need to chain a bunch of if statements.
For more complicated scenarios, you might prefer something like PredicateBuilder
You can use a pattern like this:
dbContext.Students.Where(c=>(crit.name == null || c.name ==crit.name) && ...)
A search criterion which is null will give a subexpression which is always true.

Storing a complex boolean in a variable

I have a very lengthy boolean variable which looks something like:
c.Card != null && slot.Card.CardId == c.Card.CardId && slot.E1Number == c.E1Number && slot.Capacity == c.Capacity && slot.PacketLinkCapacity == c.PacketLinkCapacity && slot.TrafficType == c.TrafficType && slot.TxFrequency == c.RxFrequency && slot.RxFrequency == c.TxFrequency && slot.E1Number != null && slot.Capacity != null && slot.ProtectionMode == c.ProtectionMode
Since this condition needs to be checked frequently I keep writing the same thing over and over again. Is it possible to store this in a variable so I can just reuse that whenever I need?
Yes, you can. Just create a method that does this check, or if you want to pass that function around, use Func<bool>:
Func<bool> f = new Func<bool>( () => YourLengthyMethod() );
Try something like this:
private static Expression<Func<Slot, bool>> Filter(filter)
{
return cat => [...your code for filtering...];
}
I would recommend you to separate this and write it to another method that you will call it when you want to do all these checks. It's not good practice to have repeated code in your project.
For example, in your case, you can give as parameters to this method the info (c.Card, slot.Card.CardId etc) and do the right checking over there.
Smart(ish) usage of C# features:
class EvaluatedBoolean
{
private readonly Func<bool> _evaluation = () => false;
public EvaluatedBoolean(Func<bool> evaluation)
{
_evaluation = evaluation;
}
public static implicit operator bool(EvaluatedBoolean eb)
{
return eb._evaluation.Invoke();
}
}
Then:
var eval = new EvaluatedBoolean(() => /*your conditions here*/);
...
PerformSomeWorkIfCondition(eval);
...
void PerformSomeWorkIfCondition(bool condition)
{
if (condition)
{
//do something
...

Using Linq to replace foreach statement

I'm a little bit confused by the explanations of linq online so I figured I'd make a thread.
I want to replace my foreach statements with linq statements as I'm sure doing little things like this will make me program better.
Example of what I'd like to use LINQ with:
public bool CheckForAccount(int accountID)
{
bool status = false;
foreach (AccountHolders accountHolder in AccountHoldersList)
{
if (accountHolder.AccountNumber == accountID)
{
status = true;
break;
}
}
return status;
}
Please can you provide an explanation of how it works too so I understand what you're doing.
Thanks in advance!
So probably the most succinct statement would be:
public bool CheckForAccount(int accountId)
{
return this.AccountHolderList.Any(x => x.AccountNumber == accountId);
}
In English this says
Check AccountHolderList for any cases where the AccountNumber property is equal to accountId. If there are any, return true, otherwise return false.
both
AccountHolderList.Any(x => x.AccountNumber == accountId);
and
AccountHoldersList.Exists(p => p.AccountNumber == accountId);
work equally well. Here is a more in-depth explanation of the differences:
Linq .Any VS .Exists - Whats the difference?
You could also use this:
var match = AccountHolderList.FirstOrDefault(x => x.AccountNumber == accountId);
return match != null ? true : false;
if you want to get a reference to the match as well. This works assuming accountId is unique

FindAll multiple conditions

So I'm trying to use FindAll to return a list of objects that match. It works great when I only use one condition, for example
patientstatus = statuslist.FindAll(delegate(StatusReader.onestatus p1)
{
return p1.WL_ID == patlist[i].wl_id;
});
But I would like to use multiple conditions, some of which contain if statements and I can't figure out how. It seems like the format needs to have a return statement, and the example from Microsoft (http://msdn.microsoft.com/en-us/library/fh1w7y8z.aspx) only uses one condition.
I could either have multiple FindAll methods for each condition, or just make a loop that scans through all the values myself, but there's got to be an obvious thing I'm missing, right?
I'm not quite sure what "if loops" are, but you can always just stitch them together:
patientstatus = statuslist.FindAll(delegate(StatusReader.onestatus p1)
{
if(p1.WL_ID != patlist[i].wl_id)
return false;
if(otherStuff != 5)
return false;
for(var x in p1.stuff)
if(x == 7)
return false;
return true;
});
Try the following:
var patientStatus = statusList.Where(p => p
{
// Put in logic here as you need
return p.WL_ID == patlist[i].wl_id || p.YourSecondProperty == WhateverYouWantToCheck;
}
You can think about something like
public abstract class Condition
{
public abstract bool Sutisfied(StatusReader.onestatus status);
}
public class Condition1 : Condition
{
public override bool Sutisfied(StatusReader.onestatus status) {
//check for something and return
}
}
public class Condition2 : Condition
{
public override bool Sutisfied(StatusReader.onestatus status) {
//check for something and return
}
}
After can have a list of conditions, like
List<Condition> conditions =
new List<Condition>{new Conditon1(), new Condition2()}
and after this list use inside
patientstatus = statuslist.FindAll(delegate(StatusReader.onestatus status)
{
return conditions.TrueForAll(c=>c.Sutisfied(status));
});
Your delegate simply needs to return true for a match to your conditions and false for a non-match to your conditions. It doesn't have to be a single line return statement. You can create boolean values, have if statements, for loops, and anything else you want in your delegate - so long as it returns true or false along all code paths.
So you can do as many if statements or loops as you want.
If you really want to maintain it as one line, you can do something like the following...
return (condition1 == condition1) || (condition2 == condition2) || (condition3 == condition3);

Categories