Linq SelectMany query not working - linq

I have the following database tables where the relationship between Topic and Comment is 1:N
Category
id
name
Topic
id
title
category_id
Comment
id
details
topic_id
deleted (boolean)
I want a query to count total comments in each category. I have the following LINQ query but it doesn't work:
#foreach (var cat in Model.AllPermissionSets.Keys)
{
var commentCount = cat.Topics.Where(c => c.Comments.deleted != 0).SelectMany(x => x.Comments).Count();
#* some other stuff *#
}
In Visual studio I get error IList<Comment> doesn not contain a definition for deleted...
What's the correct syntax to do the above?

Comments is a collection type property on each Topic instance. This collection does not have a deleted property. But each item in the collection(a single instance of Comment) has it.
var commentCount = cat.Topics.Where(c => c.Comments.Any(s=>!s.deleted))
.SelectMany(x => x.Comments).Count();
This will give you the count of non deleted comments from all the posts of the category.
The first part, cat.Topics.Where(c => c.Comments.Any(s=>!s.deleted)) will give you a filtered list of Topic collection which has at least one non deleted comment. In the second part, you are selecting the Comments of All those posts and doing a Count.
Copied from Ivan Stoev's comment below.
The below query will also produce the same result, but more clean.
var commentCount =cat.Topics.SelectMany(t => t.Comments.Where(c => !c.Deleted)).Count();

Related

LINQ query left joining two tables with concatenation

I am using this as a reference -- how concatenate multiple rows in LINQ with two tables?
I have the exact same needs, except that not all "printers" have "resolutions". In my particular case, I have a Lead table, which stores some basic information. Then there is a tag table, which stores tags used for the Lead. Not every lead has a tag.
This is what I have so far based on the above reference:
var leads = _dbRO.Leads.Join(_dbRO.Tags, p => p.LeadId, r => r.EntityId, (p, r) => new
{
LeadId = p.LeadId,
GigDate = p.GigDate,
Location = p.Location,
Tags = String.Join("|", _dbRO.Tags.Where(k => k.EntityId == p.LeadId)
.Select(lm => lm.TagName.ToString()))
}).Distinct();
This works well for me. However, leads without tags are NOT returned. How do I ensure all leads are returned regardless of tags. An empty string or null for Tags field would be fine.
Also if you don't mind, if I want to return the Tags in an object array, how do I do that? The reason is because there could be additional information associated with each tag, like color etc. So a simple concatenated string might not be sufficient.
Thanks a bunch!
I've figured out -- I do not need to join the tag table at all. This causes the problem. I just need to select from my Lead table and in the Select section, get the tags as I was already doing.
If you’ve declared a relationship between Lead and Tag entity types, then EF already supplies your requirements through the Include() extension method.
ctx.Leads.Include(l => l.Tags).ToList()
This requires that Lead declares a navigation property to Tag as shown below.
class Lead
{ ... public List<Tag> Tags { get; set; } }

.Where in LinQ not working correctly

I have Documents table and Signs table. Document record can be related with many records in Signs table.
Now, I want to get all records of Documents table when document ID appears in Signs table.
Here I get all documents:
var documents = (from c in context.documents select c);
Here I get all my signs and save into List:
var myDocuments = (from s in context.signs where s.UserId== id select s.ID).ToList();
This list contains collection on document ID.
And here, I'm trying to get all documents that exists in myDocuments list:
documents.Where(item => myDocuments.Contains(item.ID));
But, when I do .ToList() allways return all records (in database only exists one compatible record)
What is wrong in LinQ statement?
The problem is that this statement doesn't modify the contents of documents, it merely returns the results (which you're not doing anything with):
documents.Where(item => myDocuments.Contains(item.ID));
documents is still the full list.
Change this line to something like:
var matchingIDDocs = documents.Where(item => myDocuments.Contains(item.ID));
And then use matchingIDDocs in place of "documents" later in your code.

How to access child entity's property in a where clause of linq expression?

I've already asked similar question but it was not very precise, so I will do it again.
Suppose I have a model where one entity includes other entity. Is there any way to access a property of child entity withing a linq query? Here is the example:
string category = "something";
IEnumerable<string> Items = _itemsRepository.GetItems()
.Where(i => i.Product.Category == category)
.OrderBy(i => i.ItemId);
In a clause "Where" I want to access child entity's property "Category", (this chunk of code doesn't work). Table "Items" contains foreign key "ProductId" of table "Products".
So here I want to select items which product's category equals "something". How can it be done?
Thank You in advance!
IEnumerable<Item> Items = _itemsRepository.GetItems()
.Where(i => category == null ||
(i.Product != null &&
i.Product.Category == category))
.OrderBy(i => i.ItemId);
As others suggested in their comments providing more info about the error will help us answer your question.
Are you getting a null reference exception?
If so, make sure you are including your child entity/navigation property in the linq statement or query that is being executed in the method _itemsRepository.GetItems().
Entity framework won't load your related objects unless you tell it to. I recommend that you eagerly load your property using the .Include() method. You need to do this as part of whatever query you are executing inside of GetItems(). You can also use lazy loading to get access to your sub properties. Here is an article that explains it some.
http://msdn.microsoft.com/en-us/magazine/hh205756.aspx
Hope that helps.
You are missing a select clause
IEnumerable<string> Items = _itemsRepository.GetItems()
.Where(i => i.Product.Category == category)
.OrderBy(i => i.ItemId)
.Select(i => i.Name); //<- or whatever string property you expect to get out
Otherwise, you should not have IEnumerable<string> but rather IEnumerable<Item> or what your entity is called.

How do I get like to return data from multiple tables?

I'm trying to modify the following query to return data from multiple tables.
I have the main contacts table where all contact details are stored. Companies are also stored in the Contacts table. Companies are mapped to Contacts in a join table named CompanyPersonMap.
I also have an attributes table where the contacts are assigned certain attributes, possibly many per contact.
Now, I want to return all the contacts that have a certain attribute assigned PLUS the person's Company name and address found in a different row in the Contacts table.
This code works but only returns the contactId. Fine for what I needed but I can't figure out how to modify it for the above requirement.
var peopleAndAddresses = Contacts.Where(c => c.AssignedAttributes
.Any (aa => aa.AttributeID == 1153))
.Select(x => x.ContactID);
Any suggestions for doing this?
Thanks!
Edit-
I tried working on the .Select() statement and found the path from the contact to its organzation via the PersonOrgMap table. Unfortunately, I can't get any of the data about the organization. I tried .Any(), .All(), and .FirstOrDefault(). Any suggestions?
.Select (c => new {c.FirstName, c.LastName, c.PersonOrgMap.FirstOrDefault (pom => pom.ChildContactID) })
This code below will get you all Contacts that have at least one AssignedAttribute with an AttributeID of 1153. I think this is what you're looking for.
var peopleAndAddress = Contacts
.Where(c => c.AssignedAttributes.Any(aa => aa.AttributeID == 1153))
.Select(c => c);
However, your statement "name and address found in a different row in the Contacts table" makes me think that this might not be what you need. Can you just get what the Company Name and Address is from properties on Contact?

LINQ Query to find all tags?

I have an application that manages documents called Notes. Like a blog, Notes can be searched for matches against one or more Tags, which are contained in a Note.Tags collection property. A Tag has Name and ID properties, and matches are made against the ID. A user can specify multiple tags to match against, in which case a Note must contain all Tags specified to match.
I have a very complex LINQ query to perform a Note search, with extension methods and looping. Quite frankly, it has a real code smell to it. I want to rewrite the query with something much simpler. I know that if I made the Tag a simple string, I could use something like this:
var matchingNotes = from n in myNotes
where n.Tags.All(tag => searchTags.Contains(tag))
Can I do something that simple if my model uses a Tag object with an ID? What would the query look like. Could it be written in fluent syntax? what would that look like?
I believe you can find notes that have the relevant tags in a single LINQ expression:
IQueryable<Note> query = ... // top part of query
query = query.Where(note => searchTags.All(st =>
note.Tags.Any(notetag => notetag.Id == st.Id)));
Unfortunately there is no “fluent syntax” equivalent for All and Any, so the best you can do there is
query = from note in query
where searchTags.All(st =>
note.Tags.Any(notetag => notetag.Id == st.Id))
select note;
which is not that much better either.
For starters see my comment; I suspect the query is wrong anyway! I would simplifiy it, by simply enforcing separately that each tag exists:
IQueryable<Note> query = ... // top part of query
foreach(var tagId in searchTagIds) {
var tmpId = tagId; // modified closures...
query = query.Where(note => note.Tags.Any(t => t.Id == tmpId));
}
This should have the net effect of enforcing all the tags specified are present and accounted for.
Timwi's solution works in most dialects of LINQ, but not in Linq to Entities. I did find a single-statement LINQ query that works, courtesy of ReSharper. Basically, I wrote a foreach block to do the search, and ReSharper offered to convert the block to a LINQ statement--I had no idea it could do this.
I let ReSharper perform the conversion, and here is what it gave me:
return searchTags.Aggregate<Tag, IQueryable<Note>>(DataStore.ObjectContext.Notes, (current, tag) => current.Where(n => n.Tags.Any(t => t.Id == tag.Id)).OrderBy(n => n.Title));
I read my Notes collection from a database, using Entity Framework 4. DataStore is the custom class I use to manage my EF4 connection; it holds the EF4 ObjectContext as a property.

Resources