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>;
}
Related
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>();
}
I've been staring at this for awhile and not sure how to fix it.
All I'm trying to do is fill in the arptable description property where it matches the address in the device table. I am not trying to create another collection from the arptable.
Error:
The type arguments for method 'System.Linq.Enumerable.Select(System.Collections.Generic.IEnumerable, System.Func)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
Here is the offending code:
IEnumerable<ARPTABLE> GetARPTable()
{
IpTable arp = new IpTable();
IEnumerable<ARPTABLE> arptable = arp.GetArpTable();
arptable.ToList().ForEach(i =>
{
DeviceTable.Where(j => j.PhysicalAddress == i.MAC)
.Select(y =>
{
i.Description = y.Model ?? y.DeviceName;
});
});
return arptable;
}
where DeviceTable is
public ObservableCollection<Device> DeviceTable { get; set; }
Thanks for any help (or a better way).
The compiler is having trouble because your lambda expression isn't written correctly and so it fails on type inference. Unfortunately, the whole construction of the method is broken and I'm not sure I really understand what you're trying to accomplish here.
But in terms of getting it to compile, your method should look more like this:
IEnumerable<ARPTABLE> GetARPTable()
{
IpTable arp = new IpTable();
IEnumerable<ARPTABLE> arptable = arp.GetArpTable();
foreach (var i in arptable)
{
Device j = DeviceTable.FirstOrDefault(j => j.PhysicalAddress == i.MAC);
if (j != null)
{
i.Description = j.Model ?? j.DeviceName;
}
}
return arptable;
}
As a general note: do not use methods like Select() as a way to simply visit each element in the collection. The expression you give to the Select() method will not actually be evaluated unless you evaluate the IEnumerable<T> that was returned, which you did not in this case. And even if you do evaluate the IEnumerable<T>, it's an inefficient misuse of the method.
Also, while List<T>.ForEach() could be considered convenient by some, it is wasteful to convert an IEnumerable<T> to a List<T> just for the purpose of calling that method. A regular foreach statement works fine and is more efficient.
LINQ is not ment to be used for filling in data. It's a query language and so the Select method returns a new sequence. Just do it with foreach. I would image it could look like this, although I'm not exactly sure if I got the logic right.
foreach(var table in arptable)
{
var device = DeviceTable.SingleOrDefault(...);
if (device != null)
{
table.Description = device.Model ?? device.DeviceName;
}
}
As for your current form
arptable.ToList().ForEach(i =>
this is really not necessary, why cast the sequence to list if you don't have to? Just to use that ForEach? We can do better.
DeviceTable.Where(j => j.PhysicalAddress == i.MAC)
.Select(y => i.Description = y.Model ?? y.DeviceName);
This returns a new sequence, which you are not storing in any local variable. LINQ queries should not have side effect, it's against the lambda calculus, the idea behind LINQ itself.
i like the other answers. if you still want to use linq, this is how you would:
IEnumerable<ARPTABLE> GetARPTable()
{
IpTable arp = new IpTable();
IEnumerable<ARPTABLE> arptable = arp.GetArpTable();
arptable = arptable.Select(i =>
{
Device device = DeviceTable.SingleOrDefault(j => j.PhysicalAddress == i.MAC);
if (device != null)
{
i.Description = device.Model ?? device.DeviceName;
}
return i;
});
return arptable;
}
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);
}
I need to pass a parameter to a method that requires an Expression<Func<T, bool>>.
How to do I pass an expression that would always return true?
Using obj => true doesn't work because the framework complains at runtime that it cannot determine the memeber type from the True constant.
If you have a function like this
void TakeExpression<T>(Expression<Func<T, bool>> expr)
You should call it this way, specifying the T type :
TakeExpression<int>(_ => true)
It should work.
You need to define the parameter type you are passing:
(object o) => true
Or
(int a) => true
We can achieve the result as follows.
Consider context as your DbContext instance and Entity as your entity class name.
context.Entity.Where(t=> t.EntityID == t.EntityID);
By doing this the where clause will always return true and all the data will be shown.
There are two problems here:
1) If you're passing a predicate such that you always want to return true, then it's not much of a predicate. You may be able to omit whatever call you are trying to make.
2) If you want to just return true, you can simple use a more verbose lambda syntax to get what you want:
sample.AsQueryable().Where((x) => { return true; });
The more verbose syntax allows you to specify closer to an anonymous function while still being an expression.
I have an enum called OrderStatus, and it contains various statuses that an Order can be in:
Created
Pending
Waiting
Valid
Active
Processed
Completed
What I want to do is create a LINQ statement that will tell me if the OrderStaus is Valid, Active, Processed or Completed.
Right now I have something like:
var status in Order.Status.WHERE(status =>
status.OrderStatus == OrderStatus.Valid ||
status.OrderStatus == OrderStatus.Active||
status.OrderStatus == OrderStatus.Processed||
status.OrderStatus == OrderStatus.Completed)
That works, but it's very "wordy". Is there a way to convert this to a Contains() statement and shorten it up a bit?
Sure:
var status in Order.Status.Where(status => new [] {
OrderStatus.Valid,
OrderStatus.Active,
OrderStatus.Processed,
OrderStatus.Completed
}.Contains(status.OrderStatus));
You could also define an extension method In() that would accept an object and a params array, and basically wraps the Contains function:
public static bool In<T>(this T theObject, params T[] collection)
{
return collection.Contains(theObject);
}
This allows you to specify the condition in a more SQL-ish way:
var status in Order.Status.Where(status =>
status.OrderCode.In(
OrderStatus.Valid,
OrderStatus.Active,
OrderStatus.Processed,
OrderStatus.Completed));
Understand that not all Linq providers like custom extension methods in their lambdas. NHibernate, for instance, won't correctly translate the In() function without additional coding to extend the expression parser, but Contains() works just fine. For Linq 2 Objects, no problems.
I have used this extension:
public static bool IsIn<T>(this T value, params T[] list)
{
return list.Contains(value);
}
You may use this as the condition:
Where(x => x.IsIn(OrderStatus.Valid, ... )
If that set of statuses has some meaning, for example those are statuses for accepted orders, you can define an extension method on your enum and use that in your linq query.
public static class OrderStatusExtensions
{
public static bool IsAccepted(this OrderStatuses status)
{
return status == OrderStatuses.Valid
|| status == OrderStatuses.Active
|| status == OrderStatuses.Processed
|| status == OrderStatuses.Completed;
}
}
var acceptedOrders = from o in orders
where o.Status.IsAccepted()
select o;
Even if you could not give the method a simple name, you could still use something like IsValidThroughCompleted. In either case, it seems to convey a little more meaning this way.
Assumnig that the enum is defined in the order you specified in the question, you could shorten this by using an integer comparison.
var result =
Order.Status.Where(x =>
(int)x >= (int)OrderStatus.Valid &
& (int)x <= (int)OrderStatus.Completed);
This type of comparison though can be considered flaky. A simply re-ordering of enumeration values would silently break this comparison. I would prefer to stick with the more wordy version and probably clean up it up by refactoring out the comparison to a separate method.
You could put these in a collection, and use:
OrderStatus searchStatus = new[] {
OrderStatus.Valid,
OrderStatus.Active,
OrderStatus.Processed,
OrderStatus.Completed };
var results = Order.Status.Where(status => searchStatus.Contains(status));