I am seeking any advice or tips on the following method I have that is using LINQ to find a certain property in a Collection that is null and then go through the results (sub-list) and execute a method on another property from the same Collection.
private void SetRaises()
{
if (employeeCollection != null)
{
var noRaiseList = employeeCollection .Where(emp => emp.Raise == null).ToList();
foreach (var record in noRaiseList )
{
CalculateRaise(record);
}
}
}
public void CalculateRaise(Employee emp)
{
if (emp!= null)
emp.Raise = emp.YearsOfService * 100;
}
The part I don't like in the first method, SetRaises(), is the following snippet:
foreach (var record in noRaiseList )
{
CalculateRaise(record);
}
Is there a way to integrate that part into my LINQ expression directly, i.e. some extension method I am not aware of?
Thank you!
The first thing you could do would be: don't generate an intermediate list:
var pending = employeeCollection.Where(emp => emp.Raise == null);
foreach (var record in pending)
{
CalculateRaise(record);
}
which is identical to:
foreach (var record in employeeCollection.Where(emp => emp.Raise == null))
{
CalculateRaise(record);
}
This is now non-buffered deferred execution.
But frankly, the LINQ here isn't giving you much. You could also just:
foreach(var emp in employeeCollection)
{
if(emp.Raise == null) CalculateRaise(emp);
}
If you don't need list of employees without Raise you can do this in one line:
employeeCollection.Where(emp => emp.Raise == null).ToList().ForEach(x => x.Raise = x.YearsOfService * 100);
You could use the ForEach chain-method. But that's only sugar syntax.
employeeCollection.Where(emp => emp.Raise == null)
.ToList()
.ForEach(record => CalculateRaise(record))
It should be something like this:
var noRaiseList = employeeCollection .Where(emp => emp.Raise == null).ToList().ForEach(e=>e.Raise = e.YearsOfService * 100);
Related
So I have a search-input and checkboxes that passes the values to the controller when there are inputs. And I want to use these values to get something back from the database. The search-input is a string and it works and intended. Here is the code for the search-input:
public async Task<ViewResult> Index(string searchString, List<int> checkedTypes)
{
var products = from p in _db.Products select p;
ViewData["CurrentFilter"] = searchString;
if (!string.IsNullOrEmpty(searchString))
{
products = products.Where(p => p.Name.ToLower().Contains(searchString));
}
return View(products);
}
However the checkboxes values are stored in a list. So basically I want to do the same as the code above, but with a list. So basically an idea is like this:
if(checkedTypes != null)
{
foreach (var i in checkedTypes)
{
products = products.Where(p => p.TypeId == i));
}
}
If I do it like the code above, I just get the last (i) from the loop. Another solution I did was this:
if(checkedTypes != null)
{
var temp = new List<Product>();
foreach (var i in checkedTypes)
{
temp.AddRange(products.Where(p => p.TypeId == i));
}
products = temp.AsQueryable();
}
But when I did it like that I get this error:
InvalidOperationException: The provider for the source IQueryable doesn't implement IAsyncQueryProvider. Only providers that implement IAsyncQueryProvider can be used for Entity Framework asynchronous operations.
So anyone have a solution that I can use? Or is there a better way to handle checkboxes in the controller?
Assuming you are using EF Core (also the same is true for linq2db) - it supports translating filtering with local collection, i.e. Where(x => checkedTypes.Contains(x.SomeId)).
If you have "and" logic to filter by searchString and checkedTypes than you can conditionally add Where clause:
if (!string.IsNullOrEmpty(searchString))
{
products = products.Where(p => p.Name.ToLower().Contains(searchString));
}
if(checkedTypes != null)
{
products = products.Where(p => checkedTypes.Contains(p.TypeId));
}
P.S.
Also you should be able to change your first line to:
var products = _db.Products.AsQueryable();
i'm sorry if this question has already been asked, but i'm in trouble with my method of updating collection in Entity Framework.
Let me explain the situation :
- I have for example one model CUSTOMER with some properties and a collection of ORDERS (for example).
- Let's imagine we have an admin page on wich we can edit all the ORDERS for a customer, and when we submit the form, it will send us back the object CUSTOMERS with updated ORDERS (some added, some updated and some deleted).
For the moment i use something like this in order to compare old collection and new collection and determine which object i need to delete/update/add
var toRemove = new List<ORDERS>();
var toAdd = new List<ORDERS>();
foreach (
var order in
oldList.Where(
order =>
newList.FirstOrDefault(t => t.link_id == order.link_id) == null))
{
toRemove.Add(order);
}
foreach (
var order in
newList.Where(
order =>
oldList.FirstOrDefault(t => t.link_id == order.link_id) == null))
{
toAdd.Add(order);
}
foreach (var ORDERSe in toRemove)
{
bdd.ORDERS.Remove(ORDERSe);
}
foreach (var ORDERSe in toAdd)
{
ORDERSe.pjt_id = project_id;
bdd.ORDERS.Add(ORDERSe);
}
foreach (
var order in
newList.Where(
order =>
oldList.FirstOrDefault(t => t.link_id == order.link_id) != null))
{
var child = oldList.FirstOrDefault(t => t.link_id == order.link_id);
bdd.Entry(child).CurrentValues.SetValues(order);
}
But i'm unconfortable with this, because in my mind, entity framework should be able to do the work for me !
I was hoping something like :
customer.orders = newOrders;
Did i missed anything about entity framework or ?
Because when i do this, it just duplicate my orders.
Thanks in advance for your answer.
You can certainly make it cleaner using .Except() and .Intersect(), but the concept doesn't really change, AFAIK you still have to individually remove, update & add the entries in loops...
var oldList = new List<ORDERS>();
var newList= new List<ORDERS>();
var IdsToRemove = oldList.Select(t => t.link_id).Except(newList.Select(t => t.link_id));
var IdsToAdd = newList.Select(t => t.link_id).Except(oldList.Select(t => t.link_id));
var IdsToUpdate = newList.Select(t => t.link_id).Intersect(oldList.Select(t => t.link_id));
//remove
bdd.orders.where(x => IdsToRemove.Contains(x.link_id)).ForEach(x => bdd.Remove(x));
//add
foreach(var order in newList.Where(x -> IdsToAdd.Contains(x.link_id))
{
bdd.Orders.Attach(order);
bdd.Entries(order).EntityState = EntityState.Added;
}
//update
foreach(var order in newList.Where(x -> IdsToUpdate .Contains(x.link_id))
{
bdd.Orders.Attach(order);
bdd.Entries(order).EntityState = EntityState.Modified;
}
bdd.SaveChanges();
But i'm unconfortable with this, because in my mind, entity framework
should be able to do the work for me !
In fact, EF does the Work for you. Using the data context SaveChanges method EF should be able to save all your changes at once:
DbContext.SaveChanges()
For your convinience you can still override this method. Internally you should use something like this:
public override int SaveChanges()
{
var changeSet = ChangeTracker.Entries<IAuditable>();
if (changeSet != null)
{
foreach (var entry in changeSet.Where(c => c.State != EntityState.Unchanged))
{
entry.Entity.ModifiedDate = DateProvider.GetCurrentDate();
entry.Entity.ModifiedBy = UserName;
}
}
return base.SaveChanges();
}
Is there any way to reduce the following code into Linq form?
foreach (var current in currentWhiteListApps)
{
var exists = false;
foreach (var whiteList in clientSideWhiteLists)
{
if (current.appID.Equals(whiteList.appID))
{
exists = true;
}
}
if (!exists)
{
deleteList.Add(current);
}
}
All I can think of is:
currentWhiteListApps.Select(x => {
var any = clientSideWhiteLists.Where(y => y.appID.Equals(x.appID));
if (any.Any())
deleteList.AddRange(any.ToArray());
return x;
});
Reason For LINQ
LINQ is far more readable than nested foreach loops, and requires less code. So this is the reason I would like it in LINQ
var deleteList = currentWhiteListApps.Where(x =>
clientSideWhiteLists.All(y => !x.appID.Equals(y.appID)))
.ToList();
var deleteList = currentWhiteListApps.Except(clientSideWhiteLists).ToList();
This solution assumes that both collections contains elements of the same type and this type has overriden Equals() that compares appID.
var validIds = new HashSet<int>(clientSideWhiteLists.Select(x => x.appId));
var deleteList = currentWhiteListApps.Where(x => !validIds.Contains(x.appId)).ToList();
I am trying to gransp wether I can get big refactoring advantages out of learning LINQ.
How can LINQ improve this code, which is a real-world example that is representative for a lot of code in a project I work on:
foreach (SchemeElement elem in mainDiagram.Elements)
{
if (elem.SubType == EElemSubType.BusBar)
{
if (connPts.Busbars.ContainsKey(elem.ConnectionPointId))
{
if (!taAddrList.TAAddressList.ContainsKey(elem.Key))
{
taAddrList.TAAddressList.Add(elem.Key, new TAAddress());
}
taAddrList.TAAddressList[elem.Key] = connPts.Busbars[elem.ConnectionPointId];
}
} // if busbar
} // foreach element
For Clarity:
taAddrList.TAAddressList is of type Dictionary<ElemKey, TAAddress>
where ElemKey is a two-component type that consists of two int ID's.
connPts.Busbars is of type Dictionary<int, TAAddress>
See for yourself:
var query = from element in mainDiagram.Elements
where element.SubType == EElemSubType.BusBar
where connPts.Busbars.ContainsKey(element.ConnectionPointId)
select element;
foreach (var element in query)
{
// by accessing immidiatly in a dictionary (assuming you are using one), you can either insert or update
taAddrList.TAAddressList[element.Key] = connPts.Bushbars[elem.ConnectionPointId];
}
Well this depends, its certainly alot easier to write that sort of stuff in LINQ, but the depends part is on whether TAddressList is just a Dictionary... if it were you can get that dictionary easily:
var dictionary = mainDiagram.Elements.Where(e => e.SubType == EElemSubType.BusBar)
.ToDictionary(k => k.Key, e => connPts.BusBars[e.ConnectionPointId])
If you have to add to TAddressList in exactly the manner you gave in your example, you simply need to ForEach over the list
mainDiagram.Elements.Where(e => e.SubType == EElemSubType.BusBar && !taAddrList.TAAddressList.Contains(e.Key))
.ToList()
.ForEach(e => taAddrList.TAAddressList.Add(elem.Key, connPts.BusBars[e.ConnectionPointId]));
You can use linq for selecting a list of SchemeElement:
var query = from elem in mainDiagram.Elements
where elem.SubType == EElemSubType.BusBar
&& connPts.Busbars.ContainsKey(elem.ConnectionPointId)
select elem;
foreach (SchemeElement elem in query){
if (!taAddrList.TAAddressList.ContainsKey(elem.Key))
{
taAddrList.TAAddressList.Add(elem.Key, new TAAddress());
}
taAddrList.TAAddressList[elem.Key] = connPts.Busbars[elem.ConnectionPointId];
}
var elements =
mainDiagram.Elements
.Where(e => e.SubType == EElemSubType.BusBar &&
connPts.Busbars.ContainsKey(e.ConnectionPointId))
foreach (var elem in elements)
{
if (!taAddrList.TAAddressList.ContainsKey(elem.Key))
{
taAddrList.TAAddressList.Add(elem.Key, new TAAddress());
}
taAddrList.TAAddressList[elem.Key] = connPts.Busbars[elem.ConnectionPointId];
}
var items = mainDiagram.Elements
.Where(el => el.SubType == EElemSubType.BusBar
&& connPts.Busbars.ContainsKey(el.ConnectionPointId));
items.ForEach(item =>
{
if (!taAddrList.TAAddressList.ContainsKey(item.Key))
{
taAddrList.TAAddressList.Add(item.Key, new TAAddress());
}
});
foreach (SchemeElement elem in mainDiagram.Elements.Where(r => r.SubType == EElemSubType.BusBar
&& connPts.Busbars.ContainsKey(r.ConnectionPointId)))
{
if (!taAddrList.TAAddressList.ContainsKey(elem.Key))
{
taAddrList.TAAddressList.Add(elem.Key, new TAAddress());
}
taAddrList.TAAddressList[elem.Key] = connPts.Busbars[elem.ConnectionPointId];
} // foreach element
Code bellow is not tested, I assumed finally addresses want to go a specific Dictionary of Address objects, and address class contains two property, Key and value:
addressDic = mainDiagram.Elements.Where(x=>x.SubType == EElemSubType.BusBar)
.Where(x=>connPts.Busbars.ContainsKey(x.ConnectionPointId))
.GroupBy(x=>x.Key)
.Select(x=>new {Key = x.Key,
Value = connPts.Busbars[x.Last().ConnectionPointId]})
.ToDictionary(x=>x.Key);
but as you can see, It's not very readable in linq, but depend on your power in linq, may be it's simpler than for loop.
Lets say I have a collection of Messages which has the properties "UserID" (int) and "Unread" (bool).
How can I use LINQ extension methods to set Unread = false, for any Message in the collection in whose UserID = 5?
So, I know I can do something like:
messages.Any(m => m.UserID == 5);
But, how do I set the Unread property of each of those with an extension method as well?
Note: I know I should not do this in production code. I'm simply trying to learn some more LINQ-fu.
Actually, this is possible using only the built-in LINQ extension methods without ToList.
I believe that this will perform very similarly to a regular for loop. (I haven't checked)
Don't you dare do this in real code.
messages.Where(m => m.UserID == 5)
.Aggregate(0, (m, r) => { m.Unread = false; return r + 1; });
As an added bonus, this will return the number of users that it modified.
messages.Where(m => m.UserID == 5).ToList().ForEach(m => m.Unread = false);
Then submit the changes.
Standard LINQ extension methods doesn't include side effects aimed methods. However you can either implement it yourself or use from Reactive Extensions for .NET (Rx) like this:
messages.Where(m => m.UserID == 5).Run(m => m.Unread = false);
As there is no explicit extension method that does a ForEach, you are stuck with either using a secondary library, or writing the foreach statement on your own.
foreach (Message msg in messages.Where(m => m.UserID == 5))
{
msg.Unread = false;
}
If you really want to use a Linq statement to accomplish this, create a copy the collection using the ToList() method, accessing the ForEach() method of the List type:
messages.Where(m => m.UserID == 5).ToList().ForEach(m => m.Unread = false);
or place the side-effect in a Where() statement:
messages.Where(m =>
{
if (m.UserID == 5) { m.Unread = false; return true; }
return false;
});
In either case, I prefer to use the explicit foreach loop as it doesn't make unnecessary copies and is clearer than the Where hack.
With LINQ you can't because LINQ is a query language/extension. There is however a project called MoreLinq, which defines an extension method called ForEach which allows you to pass an action which will be performed on every element.
So, you could do with MoreLinq:
messages.Where(m => m.UserID == 5).ForEach(m => m.Unread = false);
Best Regards,
Oliver Hanappi
This answer is in the spirit of providing a solution. On could create an extension which does both the predicate (Where extension) to weed out the items and the action needed upon those items.
Below is an extension named OperateOn which is quite easy to write:
public static void OperateOn<TSource>(this List<TSource> items,
Func<TSource, bool> predicate,
Action<TSource> operation)
{
if ((items != null) && (items.Any()))
{
items.All (itm =>
{
if (predicate(itm))
operation(itm);
return true;
});
}
}
Here is it in action:
var myList = new List<Item>
{ new Item() { UserId = 5, Name = "Alpha" },
new Item() { UserId = 5, Name = "Beta", UnRead = true },
new Item() { UserId = 6, Name = "Gamma", UnRead = false }
};
myList.OperateOn(itm => itm.UserId == 5, itm => itm.UnRead = true);
Console.WriteLine (string.Join(" ",
myList.Select (itm => string.Format("({0} : {1})",
itm.Name,
itm.UnRead ))));
/* Outputs this to the screen
(Alpha : True) (Beta : True) (Gamma : False)
*/
...
public class Item
{
public bool UnRead { get; set; }
public int UserId { get; set; }
public string Name { get; set; }
}
You should be able to just do it in a Select(), remember the lambda is a shortcut for a function, so you can put as much logic in there as you want, then return the current item being enumerated. And... why exactly wouldn't you do this in production code?
messages = messages
.Select(m =>
{
if (m.UserId == 5)
m.Unread = true;
return m;
});