How to set many conditions in Func as argument in extesion method? - c#

I have asked this
the problem was solved and Works with one condition, but if I try this:
myOC.Insert(myNewElement, (x=> x.ID != 1 && x.Name.CompareTo(myString) > 0))
In this case I get an error that says that the delegate Function does not take 1 argument.
My Extension method is:
public static void Insert<T>(this ObservableCollection<T> paramOC, T paramNewElement, Func<T, bool> condition)
{
//Code
}
How can I set many conditions in the funcion?
thank you so much.

Try to use this
myOC.Insert(myNewElement, (x=> x.ID != 1 && (x.Name.CompareTo(myString) > 0)))

Related

Use WhereIf for multiple condition in c#

Hi can someone help me how best we can use whereif in LINQ, here I have a code which works fine, but I want to convert this query with WhereIf.
public async Task LoadQuery(IEnumerable<string> codes)
{
var query = _dBContext.QueryTable.Where(x => !x.InActive).AsQueryable();
if (codes!= null && codes.Any())
query = query.Where(x => codes.Contains(x.FirstCode) || query.Contains(x.SecondCode));
else
query = query.Where(x => !x.HasException.HasValue);
var data = query.ToList();
}
I have tried it with WhereIF ienumerable but not succeed. Here is the link which I followed.
https://extensionmethod.net/csharp/ienumerable-t/whereif
WhereIf isn't really suitable for your case, for 2 reasons:
You're calling two different functions on your if-else, while WhereIf is built to accept a single function (predicate) to be executed if some condition is satisfied.
WhereIf is an extension method for IEnumerable<TSource>, while your'e trying to use it as an extension method for IQueryable<TSource>.
If you insist, you'd have to define an extension method for IQueryable<TSource>, and in doing so, just define it as WhereIfElse:
public static class ExtensionMethods
{
public static IQueryable<TSource> WhereIfElse<TSource>(this IQueryable<TSource> source, bool condition, Func<TSource, bool> predicateIf, Func<TSource, bool> predicateElse)
{
if (condition)
return source.Where(predicateIf).AsQueryable();
else
return source.Where(predicateElse).AsQueryable();
}
}
So, let's say that query's type is IQueryable<Item> (replace Item with your actual type):
public async Task<List<Item>> LoadQuery(IEnumerable<string> codes)
{
var query = _dBContext.QueryTable.Where(x => !x.InActive).AsQueryable();
query = query.WhereIfElse(
// condition
codes != null && codes.Any(),
// predicateIf
(Item x) => codes.Contains(x.FirstCode) || codes.Contains(x.SecondCode),
// predicateElse
(Item x) => !x.HasException.HasValue
);
var data = query.ToList();
return data;
}
P.S. note I changed your return value, though there still isn't an await.
bool condition = codes!= null && codes.Any();
var data = _dBContext.QueryTable
.WhereIf(condition, a=> codes.Contains(a.FirstCode) || codes.Contains(a.SecondCode))
.WhereIf(!condition, a=> !a.HasException.HasValue && !a.InActive).ToList();

Method for the pattern: "if (obj is Abc && (obj as Abc) ...)"

Here's two simple classes:
class Abc { public int x; }
class Bcd { public int y; }
Given that obj is of type object, here's a couple of examples of testing for Abc or Bcd with certain characteristics:
if (obj is Abc && (obj as Abc).x > 0 && Math.Pow((obj as Abc).x, 2) > 100)
Console.WriteLine(123);
if (obj is Bcd && (obj as Bcd).y > 0 && Math.Pow((obj as Bcd).y, 2) > 100)
Console.WriteLine(234);
What's a good way to deal with this pattern of:
if (obj is Abc && (obj as Abc).SomePropertyOrMethod && ...
One approach is an Is extension method:
public static bool Is<T>(this object obj, Func<T, bool> pred) where T : class
=> (obj is T) && pred(obj as T);
With this, the above examples can be written as:
if (obj.Is<Abc>(abc => abc.x > 0 && Math.Pow(abc.x, 2) > 100))
Console.WriteLine(123);
if (obj.Is<Bcd>(bcd => bcd.y > 0 && Math.Pow(bcd.y, 2) > 100))
Console.WriteLine(234);
In this way the (obj as ...) expressions are not repeated.
Are there other approaches to this pattern?
This pattern matching proposal seems like it would handle this well (see section 8.1).
I feel this takes a simple thing and makes it complicated. Also the namespace pollution resulting from extending object isn't nice. Further, this is a misuse of as. This should be a throwing cast (e.g. ((Abc)obj)) because we expect the cast to always succeed. A throwing cast has an assertion and a nice exception for that built-in. It's documenting the fact that this is expected to succeed.
An alternative would be to declare a variable:
var abc = obj as Abc;
if (abc != null && abc.x > 0 && Math.Pow(abc.x, 2) > 100)
Console.WriteLine(123);
Seems simple enough. I see no problems with that.
That said the extension method approach can be useful in places where you cannot easly declare a variable, for example in certain queries or in deeply nested expressions. That's generally undesirable but it sometimes happens.
I am not sure what's best/worst but (obj as Abc).x may end up in NullReferenceException if the cast fails. One way I see is breaking down the condition check like below:
Abc a = obj as Abc;
if (a != null && a.x > 0 && Math.Pow(a.x, 2) > 100)
Console.WriteLine(123);
That way, no need of checking the condition obj is Abc since a != null will be true only when obj as Abc casting successes.
Add some static checking:
public static bool Is<T, TBase>(this TBase obj, Func<T, bool> pred)
where TBase : class
where T : class, TBase
{
var t = obj as T;
return t != null && pred(t);
}
Use like this:
TypeBase obj;
...
if (obj.Is((TypeA obj) => ...))

Extension Method For IQueryable not working

I have written an extension method:
public static IQueryable<TSource> ConditionalDefaultEmpty<TSource>(this IQueryable<TSource> source, bool condition)
{
return condition ? source.DefaultIfEmpty() : source ;
}
And then I called the method, something like this :
var q = from sr in myDb.tblStudentsRegInfos
from de in myDb.tblDisciplinesEvents.Where(e => sr.Serial == e.tblStudentsRegInfoRef
&& e.tblStudentsRegInfoRef == studentRegId
&& (!forStudent || e.PublishOnInternet)
&& (!formDate.HasValue || e.RegDate >= formDate)
&& (!toDate.HasValue || e.RegDate <= toDate))
.ConditionalDefaultEmpty(noPrintAll)
join dt in myDb.tblDisciplinesTitles on de.tblDisciplinesTitlesRef equals dt.Serial
where sr.Serial == studentRegId
group sr by new
{
...
}
into std
select new
{....
};
But I get this error :
Member access [column name] is not legal ....
How can I fix this issue ?
Update : I had understand that EF Cannot Compile to IQueryable...
You just can't add your custom extension method and hope that Entity framework will be able to translate it into SQL, though that would be pretty cool, atleast for cases like these.
That said, you can turn IQueryable into IEnumerable:
.AsEnumerable()
.ConditionalDefaultEmpty(noPrintAll)

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
...

Custom function in Entity Framework query sometimes translates properly, sometimes doesn't

I have this function:
public static IQueryable<Article> WhereArticleIsLive(this IQueryable<Article> q)
{
return q.Where(x =>
x != null
&& DateTime.UtcNow >= x.PublishTime
&& x.IsPublished
&& !x.IsDeleted);
}
And it works just fine in this query:
from a in Articles.WhereArticleIsLive()
where a.Id == 5
select new { a.Title }
But it doesn't work in this only slightly more complex query:
from s in Series
from a in Articles.WhereArticleIsLive()
where s.Id == a.SeriesId
select new { s, a }
I get this error message:
NotSupportedException: LINQ to Entities does not recognize the method 'System.Linq.IQueryable1[TheFraser.Data.Articles.Article] WhereArticleIsLive(System.Linq.IQueryable1[TheFraser.Data.Articles.Article])' method, and this method cannot be translated into a store expression.
Any idea why? Is there another way to consolidate query parameters like this?
Thanks in advance.
EDIT: corrections by Craig.
I'm leaving this here, because I think it's a valuable tool: Use linqkit! But not for solving this question though :-)
Instead of returning IQueryable, use Expression to factor out predicates. E.g. you could define the following static method on Article:
public static Expression<Func<Article,bool>> IsLive()
{
return x =>
x != null
&& DateTime.UtcNow >= x.PublishTime
&& x.IsPublished
&& !x.IsDeleted
}
Then, ensure to store a reference to this expression when building your query, something along the lines of (not tested):
var isLive = Article.IsLive();
from s in Series
from a in Articles.Where(isLive)
where s.Id == a.SeriesId
select new { s, a }

Categories