how to iterate with Linq to create list of objets? - c#

I'm trying to iterate from a list with Linq in order to create and return a model, with contains a list of item and a quantity of total items.
The object that must be returned is as following:
public class ListeArticlesModel
{
public List<TuileArticleModel> Items { get; set; }
public int Quantity { get; set; }
}
I'm currently stuck with this :
result.Actualites = tousLesArticlesFromDb
.ToList()
.Where(
a => a.GetPropertyValue<IEnumerable<IPublishedContent>>("ficheArticle_typeDeContenu")
.FirstOrDefault()?.Name == #EnumResources.TypeDeContenu_Actualites)
.OrderBy(a => a.CreateDate)
.Take(criteriaModel.NbrItemsParPage)
.Select(
a => new ListeArticlesModel
{
Items = new List<TuileArticleModel>
{
// returns a TuileArticleModel
getItem(a)
},
})
.FirstOrDefault();
This is not what I want. If I remove the .FirstOrDefault() I get an IEnumerable
I know I am missing something. I'm building a new ListeArticleModel for each "a" item and then I just take the first one built but I don't see how to get out of here...
(ps : I graduated a few weeks ago. I am new to C#. I know it may be basics. I am trying to learn them :D)
I tried this:
var actus = tousLesArticlesFromDb
.ToList()
.Where(
a => a.GetPropertyValue<IEnumerable<IPublishedContent>>("ficheArticle_typeDeContenu")
.FirstOrDefault()?.Name == #EnumResources.TypeDeContenu_Actualites)
.OrderBy(a => a.CreateDate)
.Take(criteriaModel.NbrItemsParPage);
This gives me an IEnumerable (using umbraco here) which contains the criteriaModel.NbrItemsParPage IPublishedContent items of type "Actualites" that I want...
Now, for each I'd like to create a new TuileArticleModel to feed the Items proptery (a List<TuileArticleModel>) of my result.Actualites...
EDIT :
I think I just resolved my problem by exposing it to you guys, and reading the comments. So I did this :
var actus = tousLesArticlesFromDb
.ToList()
.Where(
a => a.GetPropertyValue<IEnumerable<IPublishedContent>>("ficheArticle_typeDeContenu")
.FirstOrDefault()?.Name == #EnumResources.TypeDeContenu_Actualites)
.Take(criteriaModel.NbrItemsParPage);
result.Actualites.Items = actus.Select(a => {return getItem(a); }).ToList();
or in one statement :
result.Actualites = new ListeArticlesModel
{
Items = tousLesArticlesFromDb
.Where
(
a => a.GetPropertyValue<IEnumerable<IPublishedContent>>("ficheArticle_typeDeContenu")
.FirstOrDefault()?.Name == #EnumResources.TypeDeContenu_Actualites
)
.OrderBy(a => a.CreateDate)
.Take(criteriaModel.NbrItemsParPage)
.Select(a => { return getItem(a); }).ToList(),
};

Get the resultset first:
var results = tousLesArticlesFromDb
.Where
(
a => a.GetPropertyValue<IEnumerable<IPublishedContent>>("ficheArticle_typeDeContenu")
.FirstOrDefault()?.Name == #EnumResources.TypeDeContenu_Actualites
)
.OrderBy(a => a.CreateDate)
.Take(criteriaModel.NbrItemsParPage);
Then pass it into the new object:
result.Actualites = new ListeArticlesModel
{
Items = results.ToList()
};
Or, if you want to do it all in one statement:
result.Actualites = new ListeArticlesModel
{
Items = tousLesArticlesFromDb
.Where
(
a => a.GetPropertyValue<IEnumerable<IPublishedContent>>("ficheArticle_typeDeContenu")
.FirstOrDefault()?.Name == #EnumResources.TypeDeContenu_Actualites
)
.OrderBy(a => a.CreateDate)
.Take(criteriaModel.NbrItemsParPage);
.ToList()
};

.ToList()
instead of
.FirstOrDefault()
Another thing to note is
tousLesArticlesFromDb
.Where( *** )
.OrderBy(a => a.CreateDate)
.Take(criteriaModel.NbrItemsParPage)
.Select(
a => new ListeArticlesModel
{
Items = new List<TuileArticleModel>
{
// returns a TuileArticleModel
getItem(a)
},
})
This will return a IQueryable, no processing has been done yet, you've just created the query for it.
Calling .ToList() will execute the IQueryable and return the result as a list.

Related

LINQ Lambda Improvement

I have a line like so:
var lstOfIds = db.TBL_AssocIncidentSpecialCat
.Where(x => x.IncidentId == incidentVm.ID)
.Select(t => t.SpecialCategoriesId)
.ToList();
This line gathers me a list of of the SpecialCategoriesIds. Then I have to do this:
incidentVm.LstSpecialCategories = db.TBL_SpecialCategories
.Where(x => lstOfIds.Contains(x.Id))
.Select(t => t.SpecialCategory)
.ToList();
Is there a way to combine these two lines into one? Even though it's only two lines of code.. I feel as though having to grab the Ids first then having to grab the associated property based on the Id is just an extra step and could be shortened to just one line. But I may be wrong.
Any help is appreciated.
UPDATE
incidentVm.LstSpecialCategories = db.TBL_AssocIncidentSpecialCat
.Where(x => x.IncidentId == incidentVm.ID)
.Join(
db.TBL_SpecialCategories,
x => new{Id = x.SpecialCategoriesId},
t => new{Id = t.Id},
(x,t) => {return t.SpecialCategory}
);
I am getting red squiggly under last part in Join:
A lambda expression with a statement body cannot be converted to an expression tree
You can combine the two lines using Join. Something like,
var result = db.TBL_AssocIncidentSpecialCat
.Join(
db.TBL_SpecialCategories,
ais => new { Id = ais.IncidentId },
sc => new { Id = sc.Id },
(ais, sc) => { return sc; }
)
.ToList();
C# Fiddle for this.
Update with Where Clause: You should use your Where condition after the Join.
var result = db.TBL_AssocIncidentSpecialCat
.Join(
db.TBL_SpecialCategories,
ais => new { Id = ais.IncidentId },
sc => new { Id = sc.Id },
(ais, sc) => new { ais = ais, sc = sc }
)
.Where(x => x.ais.IncidentId == 1)
.Select(y => y.sc)
.ToList();
You can try a LINQ query-style join:
incidentVm.LstSpecialCategories = (from aispc in db.TBL_AssocIncidentSpecialCat
join spc in db.TBL_SpecialCategories
on aispc.SpecialCategoriesId equals lspc.Id
where aispc.IncidentId == incidentVm.ID
select lspc.SpecialCategory).ToList();
I was able to figure this out with the help of some answers and me testing it on my own. Here is my solution:
incidentVm.LstSpecialCategories = db.TBL_AssocIncidentSpecialCat
.Where(t => t.IncidentId == incidentVm.ID)
.Join(db.TBL_SpecialCategories,
ik => ik.SpecialCategoriesId,
ok => ok.Id,
(ik, ok) => ok.SpecialCategory
)
.ToList();
Thank you for all of your help.

How to select a list of distinct values based on some values using linq or entity

I want to get all the Pre_Number where all Reconcile_Status related to that Pre_Number=null. In this case there should not be any item in list.If there would be some other Pre_number for eg. 7/2018 and it has two records and Reconcile_Status for those records is NULL then i should get one item in list that is 7/2018.
I tried
var NoNReconciled = context.tbl_prerelease_invoice
.Where(x => x.Reconcile_Status==null)
.Select(y => new { y.Pre_number }).Distinct().ToList();
But i got 6/2018
Well, your current attempt only checks that there is at least one record where Reconcile_Status is null, but it doesn't check that there are no records with the same Pre_number where Reconcile_Status is not null.
This should do the trick:
var NoNReconciled = context.tbl_prerelease_invoice
.Where(x => x.Reconcile_Status == null &&
!context.tbl_prerelease_invoice
.Any(y => y.Pre_number == x.Pre_number && y.Reconcile_Status != null)
).Select(y => new { y.Pre_number })
.Distinct().ToList();
No need to create anonymous object for Pre_Number. Try below code
var NoNReconciled = context.tbl_prerelease_invoice
.Where(x => x.Reconcile_Status==null)
.Select(y => y.Pre_number).Distinct().ToList();
Try this-
context.tbl_prerelease_invoice.GroupBy(r => r.Pre_number).Where(kv => kv.All(r => r.Reconcile_Status==null)).Select(kv => kv.Key).ToList();

Group by some columns depending on values in Entity Framework

I have the following simple statement in my Entity Framework code:
query = query
.Where(c => c.NotificationType == NotificationType.AppMessage)
.GroupBy(c => c.ConversationId)
.Select(d => d.OrderByDescending(p => p.DateCreated).FirstOrDefault());
It simply finds the latest Notification based on a group by with conversationId and select latest. Easy.
However, this is ONLY what I want if c.NotificationType == NotificationType.AppMessage. If the column is different than AppMessage (c.NotificationType <> NotificationType.AppMessage), I just want the column. What I truly Want to write is a magical statement such as:
query = query
.Where(c => (c.NotificationType <> NotificationType.AppMessage)
|| ((c.NotificationType == NotificationType.AppMessage)
.GroupBy(c => c.ConversationId)
.Select(d => d.OrderByDescending(p => p.DateCreated).FirstOrDefault()));
But this doesn't make sense because the GroupBy/Select is based on the first where statement.
How do I solve this?
The simplest way is to compose UNION ALL query using Concat at the end of your original query:
query = query
.Where(c => c.NotificationType == NotificationType.AppMessage)
.GroupBy(c => c.ConversationId)
.Select(d => d.OrderByDescending(p => p.DateCreated).FirstOrDefault())
.Concat(query.Where(c => c.NotificationType != NotificationType.AppMessage));
public class EntityClass
{
public int NotificationType { get; set; }
public int ConversationId { get; set; }
public DateTime Created { get; set; }
public static EntityClass GetLastNotification(int convId)
{
var list = new List<EntityClass>(); // Fill the values
list = list
.GroupBy(i => i.ConversationId) // Group by ConversationId.
.ToDictionary(i => i.Key, n => n.ToList()) // Create dictionary.
.Where(i => i.Key == convId) // Filter by ConversationId.
.SelectMany(i => i.Value) // Project multiple lists to ONLY one list.
.ToList(); // Create list.
// Now, you can filter it:
// 0 - NotificationType.AppMessage
// I didn't get what exactly you want to filter there, but this should give you an idea.
var lastNotification = list.OrderByDescending(i => i.Created).FirstOrDefault(i => i.NotificationType == 0);
return lastNotification;
}
}
you filter your list with "GroupBy" based on ConversationId. Next, create a dictionary from the result and make only one list (SelectMany). Then, you already have one list where should be only records with ConversationId you want.
Last part is for filtering this list - you wanted to last notification with certain NotificationType. Should be working :)

Dynamic linq query that Contains ALL from another list

I want to be able to find all orders with items that contain BOTH apples and oranges that I have in a list.
var itemToFind = new List<string>()
{
"apples",
"cookies"
};
How can I rewrite this so that Contains is dynamic?
This returns what I want but how do I make it loop through my list so that it is dynamic?
var query = result
.Where(o => o.OrderItems
.Any(i => i.Item.Name.Contains("apples")))
.Select(x => x)
.Where(y => y.OrderItems
.Any(b => b.Item.Name.Contains("cookies"))).ToList();
// returns 2 orders
Try something like this:
result.Where(o => o.OrderItems.Any(i => itemToFind.All(itf => i.Item.Name.Contains(itf)))).ToList()
This seems to work but not sure if that is the best way.
foreach (var item in listFacets)
{
// append where clause within loop
result = result
.Where(r => r.RecipeFacets
.Any(f => f.Facet.Slug.Contains(item)));
}

If condition check in linq

Below are the linq query. Here I want to add one condition .
Condition : If : Firstname is not empty then select list
where (d=>d.firstname=="Firstname")
else: select all list without condition
function Ponits(string Firstname)
{
pointsCore.Categories.SelectMany(c => c.Events).Select(e => new
{
e.Firstname,
e.Surname,
e.EntityNumber,
e.Eventdate
}).ToList()
}
Two options:
First, optionally use Where:
var events = pointsCore.Categories.SelectMany(c => c.Events);
if (!string.IsNullOrEmpty(firstName))
{
events = events.Where(e => e.Firstname == firstName);
}
var result = events.Select(e => new { ... });
Second: make your Where clause check firstName:
var events = pointsCore.Categories.SelectMany(c => c.Events);
.Where(e => string.IsNullOrEmpty(firstName) ||
e.Firstname == firstName)
.Select(e => new { ... });
Note that due to the lazy evaluation in LINQ, the first option won't involve fetching all the values and then querying; you're still just building up a query.
.Where(d => string.IsNullOrEmpty(Firstname) || d.firstname==Firstname)

Categories

Resources