C# Where with multiple values - c#

So for my current project i am trying to show all posts with the same subjects as the user add to favourites.
Now finding all posts for one subject works fine, but i want to get all.
var posts = db.Posts.Where(x => x.SubjectID == currentUser.Favourites.SubjectID);
This is what i tried, but unfortunately that doesn't work, it can't find subjectID, probably because currentUser's Favourites is INumberable.
Is there anyway to compare to the multiple values that might be in the currentUser's Favourites?

You could use Any inside of your Where:
db.Posts.Where(x => currentUser.Favourites.Any(f => f.SubjectID ==x.SubjectID));

Create a list of SubjectId from Favourites and then pass that list to your where clause.
var subjectIDs = currentUser.Favourites.Select(x => x.SubjectID).ToList();
var posts = db.Posts.Where(x => subjectIDs.Equals(x.SubjectID));

Related

Adding items to the list inside foreach loop

epublic ActionResult ExistingPolicies()
{
if (Session["UserId"]==null)
{
return RedirectToAction("Login");
}
using(PMSDBContext dbo=new PMSDBContext())
{
List<Policy> viewpolicy = new List<Policy>();
var userid = Session["UserId"];
List<AddPolicy> policy= dbo.AddPolicies.Where(c => c.MobileNumber ==
(string)userid).ToList();
foreach(AddPolicy p in policy)
{
viewpolicy=dbo.Policies.Where(c => c.PolicyId ==p.PolicyId).ToList();
}
Session["Count"] = policy.Count;
return View(viewpolicy);
}
}
Here the policy list clearly has 2 items.But when I iterate through foreach,the viewpolicy list only takes the last item as its value.If break is used,it takes only the first item.How to store both items in viewpolicy list??
Regards
Surya.
You can iterate through policies and add them by one to list with Add, but I would say that often (not always, though) better option would be to just retrieve the whole list from DB in one query. Without knowing your entities you can do at least something like that:
List<AddPolicy> policy = ...
viewpolicy = dbo.Policies
.Where(c => policy.Select(p => p.PolicyId).Contains(c.PolicyId))
.ToList();
But if you have correctly set up entities relations, you should be able to do something like this:
var viewpolicy = dbo.AddPolicies
.Where(c => c.MobileNumber == (string)userid)
.Select(p => p.Policy) //guessing name here, also can be .SelectMany(p => p.Policy)
.ToList();
Of course; instead of adding to the list, you replace it with a whole new one on each pass of the loop:
viewpolicy=dbo.Policies.Where(c => c.PolicyId ==p.PolicyId).ToList()
This code above will search all the policies for the policy with that ID, turn it into a new List and assign to the viewpolicy variable. You never actually add anything to a list with this way, you just make new lists all the time and overwrite the old one with the latest list
Perhaps you need something like this:
viewpolicy.Add(dbo.Policies.Single(c => c.PolicyId ==p.PolicyId));
This has a list, finds one policy by its ID number (for which there should be only one policy, right? It's an ID so I figured it's unique..) and adds it to the list
You could use a Where and skip the loop entirely if you wanted:
viewpolicy=dbo.Policies.Where(c => policy.Any(p => c.PolicyId == p.PolicyId)).ToList();
Do not do this in a loop, it doesn't need it. It works by asking LINQ to do the looping for you. It should be converted to an IN query and run by the DB, so generally more performant than dragging the policies out one by one (via id). If the ORM didn't understand how to make it into SQL you can simplify things for it by extracting the ids to an int collection:
viewpolicy=dbo.Policies.Where(c => policy.Select(p => p.PolicyId).Any(id => c.PolicyId == id)).ToList();
Final point, I recommend you name your "collections of things" with a plural. You have a List<Policy> viewpolicy - this is a list that contains multiple policies so really we should call it viewPolicies. Same for the list of AddPolicy. It makes code read more nicely if things that are collections/lists/arrays are named in the plural
Something like:
viewpolicy.AddRange(dbo.Policies.Where(c => c.PolicyId ==p.PolicyId));

How to retrieve more than one instance of a unique Id with linq?

I have a post action in my API which creates an order based off some data it retrieves from a view model.
It needs to be able to retrieve all movies from a table of 'movies' that are passed to it via the view model and create an order. The view model passes the action the Ids of the movies it needs to retrieve.
I have a working solution, and when giving the action data like this it works:
{
"movieIds": [34, 35],
"customerId": 21
}
database:
However when I give the action data which contains two or more movies with the same Id, it only ever saves one movie.
{
"movieIds": [34, 34],
"customerId": 21
}
database:
After debugging the code, i found out that it's this linq statement which is causing the problem, it only ever saves one instance of the movie to 'movies'.
movies = _context.Movies.Where(m => newRental.MovieIds.Contains(m.Id)).ToList();
Does anyone know why it does this? and how to construct a linq statement that allows it to save multiple Ids?
If you think in SQL terms, what you ask is quite complex (not too much, but probably impossible for the Entity Framework LINQ translator-to-SQL).
The simplest solution is multiply the rows after the query, C#-side.
var movies = _context.Movies.Where(m => newRental.MovieIds.Contains(m.Id)).ToList();
var movies2 = (from x in newRental.MovieIds
join y in movies on x equals y.Id
select y).ToList();
We join ("inner") the newRental.MovieIds with the movies. In this way the rows are "multiplied" when necessary.
You need to select movies from your list passed in, NOT from the list of movies in the database.
In SQL terms, what you're saying is
select * from movies where movieId in (34,34)
Which will of course return only one row.
What you need instead is to select one movie for each entry in your list. This will be less efficient for longs lists of movies, but I assume that's unlikely to be a huge problem.
movies = newRental.MovieIds
.Select(rm => _context.Movies.FirstOrDefault(m => m.Id==rm)
.Where(x => x != null) //Just make sure no entries are NULL.. optional
.ToList();
That should do what you want.
For a more convoluted, but probably more DB-efficient solution, you could instead do this:
//Get list of matches from DB into a list in one hit.
var possibleMovies = _context.Movies.Where(m=>newRental.MovieIds.Contains(m.Id)).ToList();
//Match up entries in request to DB entries.
movies = newRental.MovieIds
.Select(rm => possibleMovies.FirstOrDefault(m => m.Id==rm))
.Where(x => x != null) //Just make sure no entries are NULL.. optional
.ToList();
And that would fetch all movies in one statement then use that list to match up the requested list. You could almost certainly do this in a more terse way, but this is clear and obvious - when you look at this in 2 month's time it won't confuse you..... :)
You can try below,May this will help you
i guess newRental.MovieIds is array which contain List of MovieIds
For Specific custmore
movies = _context.Movies
.Where(m => newRental.MovieIds.Contains(m.Movie_Id) && m.customerId==21)
.ToList();
For newRental.customerId Only
movies = _context.Movies
.Where(m => newRental.MovieIds.Contains(m.Movie_Id) && newRental.customerId.contains(m.Customer_Id ))
.ToList();
Edit: Do not use this! As xanatos correctly pointed out this is an antipattern. I'll let it stand as it is for learning purposes!
This line of code returns just one element since in _context.Movies is per se only one movie with the id 34 stored. I'd change the line to:
movies = newRental.MovieIds.Select(movieId => _context.Movies.SingleOrDefault(m => m.Id == movieId)).ToList();
This way you gain a result for each new rental no matter if it's stored only once.

Searching any word using Linq

In my winform app I have a form where user can search for product by typing any text in search field. For example the product description might be stored as "Adidas aftershave 100Ml" . But user can type any thing like 100 Ml Aftershave. So I would like to query using linq to get all records where description contains any of these word(100 Ml Aftershave).
So far I have done something like this:
List<string>txtList = txtSearchTerm.Text.Split(' ').ToList();
return dbContext.productRepo.Where(t => txtList.Any(b =>
t.ProductDescription.StartsWith(b)
|| t.ProductDescription.EndsWith(b)
|| t.ProductDescription.Contains(b)
)).Select(x => x.ProductDescription).ToList();
Any other way of achieving this in better way ,making it more quick or any other improvement.
Thanks
Well, you are testing if the description starts, ends and contains something. The first two are already redundant because Contains "contains" them:
var matchingDescriptions = dbContext.productRepo
.Where(x => txtList.Any(x.ProductDescription.Contains))
.Select(x => x.ProductDescription));
Another easy optimization(for Linq-To-Objects), I would order the words by length first:
var txtList = txtSearchTerm.Text.Split(' ').OrderBy(s => s.Length).ToList()
One thing you can straightaway improve is to remove StartsWith and EndsWith , Becuase you are already doing t.ProductDescription.Contains(b) .
List<string>txtList = txtSearchTerm.Text.Split(' ').ToList();
return dbContext.productRepo.Where(t => txtList.Any(b =>
t.ProductDescription.Contains(b)
)).Select(x => x.ProductDescription).ToList();

Extract list where string is contained inside a list inside the list

Basically I have a list of objects. Let's call them meetings. Inside the meetings there is another list of objects. Let's call those participants. I want to return all meetings where a certain participant is in the list.
I want something like this:
meetings.Where(meeting => meeting.Participants.Name == "Test name").ToList();
Basically return a list of meetings, where the meeting has a participant with the name "Test name".
EDIT: I actually ended up using a MongoDB filter. Before I would just extract all the "meetings" (with a filter) and then use LINQ to filter the list. Might as well filter out the results on database level.. But this is good to know.
Are you looking for Any?
var result = meetings
.Where(meeting => meeting
.Participants
.Any(participant => participant.Name == "Test name"))
.ToList();
You can use LINQ method Any and this one line of code :
var result = meetings.Where(m => m.Participants.Any(p => p.Name == "Test name")).ToList();
Even you try the following:
meetings.Where(m => m.Participants.Any(k => k.Name == "Test Name")).ToList();

Select Contains With CSV But Keep Sort Order

I have the scenario where I have a IList<Guid> in a variable called csv which are also in a specific order that I need to keep. I am then doing a select contains like so I can get back all my topics based in the list of guids I have.
The guids are from a lucene search which are ordered by the original score from each LuceneResult. Which is why I need to keep them in this order.
var results = _context.Topic
.Where(x => csv.Contains(x.Id));
However. I lose the order the guids came in as soon as I do this. Any idea how I can do this but keep the same order I hand the list of guids to the context and get the topics back in the same order based on the topid.Id?
I have tried the following as mentioned below, by doing a join but they still come out in the same order? Please note that I am paging these results too.
var results = _context.Topic
.Join(csv,
topic => topic.Id,
guidFromCsv => guidFromCsv,
(topic, guidFromCsv) => new { topic, guidFromCsv }
)
.Where(x => x.guidFromCsv == x.topic.Id)
.Skip((pageIndex - 1)*pageSize)
.Take(pageSize)
.Select(x=> x.topic);
** UPDATE **
So I have moved away from just using and guid and am attempting to pass in my lucene model which has the score property that I want to order by. Here is what I have
public PagedList<Topic> GetTopicsByLuceneResult(int pageIndex, int pageSize, int amountToTake, List<LuceneSearchModel> luceneResults)
{
var results = _context.Topic
.Join(luceneResults,
topic => topic.Id,
luceneResult => luceneResult.Id,
(topic, luceneResult) => new { topic, luceneResult }
)
.Where(x => x.luceneResult.Id == x.topic.Id)
.OrderByDescending(x => x.luceneResult.Score)
.Skip((pageIndex - 1) * pageSize)
.Take(pageSize)
.Select(x => x.topic);
var topicResults = results.ToList();
// Return a paged list
return new PagedList<Topic>(topicResults, pageIndex, pageSize, topicResults.Count);
}
However I am now getting the following error? Is what I am doing possible?
Unable to create a constant value of type 'LuceneSearchModel'. Only primitive types or enumeration types are supported in this context.
If I understand the question correctly, you want to filter the Topics based on the csv and you want to get back the results in the same order as the csv. If so:
var results = csv
.GroupJoin(_context.Topic, guid => guid, topic => topic.Id,
(guid, topics) => topics)
.SelectMany(topics => topics);
It is important to note that this treats the _context.Topic as an IEnumerable<T>; therefore, it will fetch all topics from the database and perform the GroupJoin on the client side, not on the database.
EDIT: Based on the comment below, this answer is NOT what you want. I'll just leave the answer here for documentation.

Categories