Create empty lambda expression - c#

Consider the function:
public async Task<IEnumerable<Purchases>> GetPurchases(User user, Expression<Func<Purchases, bool>> whereClause)
{
using (var context = new UserDbContext())
{
context.Users.Attach(user);
context.Entry(user).Collection(p => p.Purchases)
.Query()
.Where(whereClause)
.Load();
if (Equals(user.Purchases, null))
return new List<Purchases>();
}
return user.Purchases;
}
In this function the parameter whereClause can at times be null, I'm wanting to check if its null and then assign an empty Expression if so. This is as close as I've come:
if (Equals(whereClause, null))
whereClause = () => { };
This was based on the question here, but for the line that makes whereClause empty I'm getting the error.
Error 7 Not all code paths return a value in lambda expression of type
'System.Func'<'Purchases,bool>'
Anyone know how this could be corrected?

"Not all code paths return a value" means your lambda isn't returning anything. I mean, it's got only one code path, and it's not a long one.
Where requires a lambda which takes one parameter, and returns bool. Where won't let you give it an empty lambda expression.
You need to give it something it can use to filter items with.
That's actually easy:
if (Equals(whereClause, null))
whereClause = o => true;
That's a where clause that always returns true, regardless of what you give it. That's probably what you want: If there's no filter, include everything. For each item in the enumeration, one at a time, Where gives the lambda the item as o. If the lambda returns true, it includes that value of o in the results.
If you want a null filter to return no items, just return false instead:
if (Equals(whereClause, null))
// Whatever it is, I'm against it.
whereClause = o => false;

whereClause must return a boolean value to use it in LinQs Where method.
Assuming you don't want to filter the list (include all items) you must return true, otherwise false:
if (Equals(whereClause, null))
whereClause = () => true;
Notice: The { and } are not necessary if you only return a value with a single statement.
But you should know that the most framework methods don't work in this way. They would throw an ArgumentNullException instead of setting a value.

Assuming that when whereClause is null you don't want to apply any filter.
using (var context = new UserDbContext())
{
context.Users.Attach(user);
context.Entry(user).Collection(p => p.Purchases)
.Query()
.Where(whereClause ?? p => true)
.Load();
if (Equals(user.Purchases, null))
return new List<Purchases>();
}

Related

How to replace switch statement of expressions

I have some code switching based on the enum value passed to it which then performs a database query (via EF)
switch(regtype)
{
case RegType.Type1:
return (Dc.ListPeople(n => n.RegistrationType1Id != null)
.DefaultIfEmpty()
.Max(n => n.RegistrationType1Id ) ?? 0) + 1;
case RegType.Type2:
return (Dc.ListPeople(n => n.RegistrationType2Id != null)
.DefaultIfEmpty()
.Max(n => n.RegistrationType2Id ) ?? 0) + 1;
...
}
Now the data model is what it is, let's look past that. RegistrationType_N_Id is an int?, ListPeople takes an argument of Expression<Func<Person, bool>>.
There are a total of 3 enum values, so it's not that bad, but even just for the mental exercise, I'd like to know if I could replace this switch statement with something more fancy.
I thought of a Dictionary<RegType, Expression<Func<something>>>, but since the first use in the db predicate is different from the 2nd in Max() got me stumped.
You can create a separate method that will take the property selector as parameter. Entity Framework cannot deal directly with delegates, since it has to translate your code into SQL, so it is necessary to deal with expression trees.
public int M(Expression<Func<Person, int?>> selector)
{
var expr = Expression.NotEqual(selector.Body, Expression.Constant(null, typeof(object)));
var lambda = Expression.Lambda<Func<Person, bool>>(expr, selector.Parameters);
return (Dc.ListPeople(lambda)
.DefaultIfEmpty()
.Max(selector)) ?? 0) + 1;
}
Usage:
switch(regtype)
{
case RegType.Type1:
return M(x => x.RegistrationType1Id);

Cannot implicitly convert type 'string' to 'bool' [If statement]

I have this validation in mvc
public static ValidationResult validaUsuariosNaLista(Reuniao item)
{
var requeridos = item.Requeridos.Select(x => x.Login).Any();
var informados = item.Informados.Select(y => y.Login).Any();
var opcionais = item.Opcionais.Select(z => z.Login ? z.Login : null).Any();
if (requeridos == informados || requeridos == opcionais || informados == opcionais)
return new ValidationResult(Resources.Validations.ValidaUsuarioMesmaLista);
return ValidationResult.Success;
}
I try make a different if in line
var opcionais = item.Opcionais.Select(z => z.Login ? z.Login : null).Any();
but show error
Error 3 Cannot implicitly convert type 'string' to 'bool'
z.Login is a string
validation is to make that the field has no value it receives null.
Without that it bursts the error that is null.
I want him to receive null without giving error for it.
It selects the z.login on the list if the same login is in the other lists he'm the error.
How can I do this "if" that way?
If z.Login is a string, then this expression is invalid:
z.Login ? z.Login : null
The first element in that expression needs to be a bool. What exactly are you trying to examine in that condition? Whether or not z.Login is null? If that's the case then you don't need a condition at all:
.Select(z => z.Login)
Since you'd just be replacing null with, well, null. Or if you want to interpret empty strings as null then you can use something like this:
.Select(z => string.IsNullOrEmpty(z.Login) ? nulll : z.Login)
Though it's not really clear what you're trying to accomplish with this line of code in the first place. .Any() is going to return a bool indicating whether or not any matching element exists in the collection at all. But since you're not using a predicate in .Any(), it's going to return true if there is any element in the collection at all. Which not only renders the .Select() pointless, but also doesn't tell you anything about that condition in the .Select().
Perhaps you are trying to find if there are any null values in the collection?:
item.Opcionais.Any(z => z.Login == null)
or any "null or empty" values?:
item.Opcionais.Any(z => string.IsNullOrEmpty(z.Login))
or the opposite, any non-empty values?:
item.Opcionais.Any(z => !string.IsNullOrEmpty(z.Login))
and so on...
z.Login is a string
validation is to make that the field has no value it receives null.
Do it like this:
Edit: Updated this section to also look for duplicates
public static ValidationResult validaUsuariosNaLista(Reuniao item)
{
var requeridos = item.Requeridos.Any(x => string.IsNullOrWhiteSpace(x.Login));
var informados = item.Informados.Any(x => string.IsNullOrWhiteSpace(x.Login));
var opcionais = item.Opcionais .Any(x => string.IsNullOrWhiteSpace(x.Login));
//does every item have a value?
if (requeridos || informados || opcionais)
return new ValidationResult(Resources.Validations.ValidaUsuarioMesmaLista);
//are all of the items unique?
if (item.Requeridos.Count() + item.Informados.Count() + item.Opcionais.Count() >
item.Requeridos.Concat(item.Informados).Concat(item.Opcionais).Distinct().Count)
{
//some items are duplicated
}
return ValidationResult.Success;
}
And, for fun, to avoid repeating code:
public static ValidationResult validaUsuariosNaLista(Reuniao item)
{
var HasEmptyValue = s => s.Any(x => string.IsNullOrWhiteSpace(x.Login));
//does every item have a value?
if (HasEmptyValue(item.Requeridos) || HasEmptyValue(item.Informados) || HasEmptyValue(item.Opcionais))
return new ValidationResult(Resources.Validations.ValidaUsuarioMesmaLista);
//are all of the items unique?
if (item.Requeridos.Count() + item.Informados.Count() + item.Opcionais.Count() >
item.Requeridos.Concat(item.Informados).Concat(item.Opcionais).Distinct().Count)
{
//some items are duplicated
}
return ValidationResult.Success;
}
Though I'm not 100% on how well type inference will work here... I'd have to see what the compiler makes of it, and I don't have your types to test with. But at worst, you'd just have to be explicit on the var declaration.
I think what you're trying to do is check if Login is null or empty. Assuming you want opcionais to be a boolean based on your .Any() statement:
var opcionais = item.Opcionais.Select(z => z.Login ? z.Login : null).Any();
should be
var opcionais = item.Opcionais.Any(z => !string.IsNullOrEmpty(z.Login));

Lambda expression in 'if' statement condition

I am new to C#, but from my understanding this code should work. Why doesn't it work?
This is an example of my code.
List<Car> cars // This has many cars initialized in it already
if (() => {
foreach(Car car in cars){
if (car.door == null) return true;
}
}){then .......}
Simply put, all I want the code to do is run the if statement if any car does not have a door.
After trying to compile I get this error:
Cannot convert lambda expression to type 'bool' because it is not a delegate type.
If you want to check if any car does not have a door then simply use Enumerable.Any - it determines whether any element of a sequence satisfies a condition:
if (cars.Any(c => c.door == null))
// then ...
Just for fun: you should execute lambda to get boolean result in if condition (but for this case use Any)
Func<bool> anyCarDoesNotHaveDoor = () => {
foreach(var car in cars)
if (car.door == null)
return true;
return false;
};
if (anyCarDoesNotHaveDoor())
// then ...
I introduced local variable to make things more clear. But of course you can make this puzzle more complicated
if (new Func<bool>(() => {
foreach(var car in cars)
if (car.door == null)
return true;
return false; })())
// then ...
Well, the error says it all. An if statement is expecting a boolean expression which a delegate is not. If you were to call the delegate (assuming it returned a bool), you would be fine. However, if does not know to call it.
The easy way to do this is with the Any LINQ extension method:
if (cars.Any(car => car.door == null))
The Any method knows to actually invoke the lambda expression on each member of the collection, and returns a bool. This makes it a valid boolean expression for the if statement.
In case you want to actually do something to cars without doors:
foreach (var car in cars.Where(car => car.door == null)) {
car.door = <whatever>;
}

Make a Search Method generic using LINQ

I have a method in my project that repeats over and over:
public PAC PAC_GetByCodiPac(string codiPac)
{
var sel = _gam.PAC.Where(pac => pac.CODI_PAC == codiPac);
if (sel.Count() > 0)
return sel.First();
return null;
}
The table PAC means (patient), so I have these methods for all the tables I have.
How can I make a generic method for this?
Thanks in advance.
Here is your generic method. Note, that as others pointed out FirstOrDefault is better than count and then first, so I'm using it here. But it's also possible to write the expression so that it mimics what your original code does. Please let me know if you need additional help with this.
public static T GetByCodi<T>(IQueryable<T> table, string codi, string fieldName) where T : class
{
// x
ParameterExpression parameter = Expression.Parameter(typeof(T), "x");
Expression currentExpression = parameter;
Type currentType = typeof(T);
PropertyInfo property = currentType.GetProperty(fieldName);
// x.CODI_xxx
currentExpression = Expression.Property(currentExpression, property);
// x.CODI_xxx == codi
currentExpression = Expression.Equal(currentExpression, Expression.Constant(codi));
// x => x.CODI_xxx == codi
LambdaExpression lambdaExpression = Expression.Lambda(currentExpression, parameter);
return table.FirstOrDefault((Func<T, bool>)lambdaExpression.Compile());
}
You use it like this:
PAC xxx = GetByCodi<PAC>(_gam.PAC, codiPac, "CODI_PAC");
Edit 1:
I changed the code according to the comment so that you can pass arbitrary ID field name in.
I see that what you asked is a very straight forward where query even doesn't require to have have it on a separate method.
Also you can simply enhance your query link the following:
public PAC PAC_GetByCodiPac(string codiPac)
{
return _gam.PAC.FirstOrDefault(pac => pac.CODI_PAC == codiPac);
}
FirstOrDefault will return the first item on the array, if not it will return null.
If you want a generic method that lets you specify any table and any predicate for records from that table then you can't really get any better than the built-in Where<T>(...) and (as others have already pointed out) the FirstOrDefault<T>(...) extension methods.
Your code would then look like so:
var result = _gam.PAC.Where(pac => pac.CODI_PAC == codiPac).FirstOrDefault();
// OR
var result = _gam.PAC.FirstOrDefault(pac => pac.CODI_PAC == codiPac);
The best you could get then, writing your own generic method, would be this:
public T FirstOrDefault<T>(IQueryable<T> source,
Expression<Func<T, bool>> predicate)
{
return source.Where(predicate).FirstOrDefault();
// OR
// return source.FirstOrDefault(predicate);
}
And that is really just redundant. Especially when your calling code would be actually longer using the helper method:
var result = FirstOrDefault(_gam.PAC, pac => pac.CODI_PAC == codiPac);
// versus
var result = _gam.PAC.FirstOrDefault(pac => pac.CODI_PAC == codiPac);
And even worse, your code is no longer using a fluent, composable syntax. This just makes readability and maintenance more difficult.
If you stick with using the IQueryable<T> extension methods then you can do composition like this:
var result = _gam.PAC
.Where(pac => pac.CODI_PAC == codiPac)
.Where(pac => pac.SomeOtherProperty == someOtherValue)
.FirstOrDefault();
// OR
var result = (from pac in _gam.PAC
where pac.CODI_PAC == codiPac
where pac.SomeOtherProperty == someOtherValue
select pac).FirstOrDefault();
One very important thing to note here is that the predicate parameter in the IQueryable<T>.Where<T>(...) extension method is of type Expression<Func<T, bool>>. This allows the IQueryable<T> provider to construct the native SQL (or other native provider query) at the very last moment before returning a result.
Not using Expression<Func<T, bool>> means that your query would be the equivalent of this:
var result =
_gam.PAC
.ToArray()
.Where(pac => pac.CODI_PAC == codiPac)
.FirstOrDefault();
And that would mean the query will load every record from the "PAC" table into memory before selecting the first filtered result and throwing out the rest of the results.
The bottom-line is that by making a generic helper method you are rewriting existing framework code and you open yourself to performance and maintenance issues while also reducing code readability.
I hope this helps.
I'm not sure if you are asking for this, but this method could be in a static class and method and so you'd be able to call it from everywhere.
An easy solution will be:
//a generic method
private PAC PAC_GetPAC(Func<PAC, bool> predicate)
{
return _gam.PAC.Where(predicate).FirstOrDefault();
}
public PAC PAC_GetPACById(long id)
{
return PAC_GetPAC(p => p.ID == id);
}
public PAC PAC_GetByCodiPac(string codiPac)
{
return PAC_GetPAC(p => pac.CODI_PAC == codiPac);
}

Where Predicates in LINQ

How can I specify conditions in Where predicates in LINQ without getting null reference exceptions. For instance, if q is an IQueryable how can I do like:
Expression<Func<ProductEntity,bool>> predicate = p => !search.CategoryId.HasValue || (search.CategoryId.HasValue && search.CategoryId == p.CategoryId);
var q2 = q.Where(predicate);
Here search is an object that holds possible search conditions that may or may not be set like search.CategoryId might not be set but if it is I want to get the products that are set by that condition.
When I do this I get null reference exceptions.
You can use the null-coalescing operator ?? to replace a possible null value with a default value. The following sets tries to match the search.Category if it exists or simply creates an "always true" expression. This will be optimized by any good Linq query provider (e.g. LinqToSql).
Expression<Func<ProductEntity,bool>> predicate = p => (search.CategoryId ?? p.CategoryId) == p.CategoryId);
var q2 = q.Where(predicate);
Another possibility would be to dynamically compose a query predicate using PredicateBuilder. That's the way I do it for searches with a similar pattern as you use:
var predicate = PredicateBuilder.True<Order>();
if (search.OrderId))
{
predicate = predicate.And(a => SqlMethods.Like(a.OrderID, search.OderID);
}
// ...
var results = q.Where(predicate);
Let's dissect the line:
Expression<Func<ProductEntity,bool> predicate = p => !search.CategoryId.HasValue
|| (search.CategoryId.HasValue && search.CategoryId == p.CategoryId)
var q2 = q.Where(predicate);
So how many ways can we get null problems?
search (your "captured" variable) could be null
p could be null, meaning there is a null in the list
you've handled the case of search.CategoryId being null (Nullable<T>)
but maybe p.CategoryId (the category on a record in the list) is null (Nullable<T>) - however, I'm not sure that this would cause a NullReferenceException
q (the list / source) could be null
So: out of 5 options you've eliminated 1; look at the other 4? There is also the definite possibility that the issue is caused by something invisible not shown in the code; for example the get could be:
public int? CategoryId {
get {return innerObject.CategoryId;}
}
and innerObject could be null; if you eliminate the other 4 (pretty easy to do), look at at this one as a last resort.

Categories

Resources